diff --git a/backend/ddang/script/prod-deploy-script.sh b/backend/ddang/script/prod-deploy-script.sh index c60842338..4bc7e9f00 100644 --- a/backend/ddang/script/prod-deploy-script.sh +++ b/backend/ddang/script/prod-deploy-script.sh @@ -4,17 +4,66 @@ ABSPATH=$(readlink -f $0) # /home/ubuntu/prod-deploy-script.sh ABSDIR=$(dirname $ABSPATH) # /home/ubuntu APPNAME="ddang-0.0.1-SNAPSHOT" APPDIR=${ABSDIR}/${APPNAME} # /home/ubuntu/ddang-0.0.1-SNAPSHOT +PORT_A=8080 +PORT_B=8081 +DEFAULT_ACTUATOR_PORT=3000 +ANOTHER_ACTUATOR_PORT=3001 -echo "구동중인 애플리케이션을 확인합니다." +if curl -s "http://localhost:${PORT_A}" > /dev/null +then + green_port=${PORT_B} + blue_port=${PORT_A} +else + green_port=${PORT_A} + blue_port=${PORT_B} +fi -CURRENT_PID=$(pgrep -f ${APPNAME}.jar) # ddang-0.0.1-SNAPSHOT.jar +if curl -s "http://localhost:${green_port}" > /dev/null +then + echo "그린 서버가 이미 동작 중입니다." + exit 255 +fi -if [ ! -z ${CURRENT_PID} ]; then - echo "기존 애플리케이션이 실행중이므로 종료합니다." - kill -15 ${CURRENT_PID} - sleep 5 +if curl -s "http://localhost:${DEFAULT_ACTUATOR_PORT}" > /dev/null +then + actuator_port=${ANOTHER_ACTUATOR_PORT} +else + actuator_port=${DEFAULT_ACTUATOR_PORT} fi -echo "애플리케이션을 실행합니다." +echo "그린 서버를 실행합니다. port number: ${green_port}" + +nohup java -jar ${ABSDIR}/${APPNAME}.jar --server.port=${green_port} --spring.profiles.active=prod --management.server.port=${actuator_port} 1>> prod.log 2>> prod_error.log & # /home/ubuntu/ddang-0.0.1-SNAPSHOT.jar + +for retry_count in $(seq 10) +do + if curl -s "http://localhost:${green_port}" > /dev/null + then + echo "Health check success ✅ port number: ${green_port}" + break + fi + + if [ $retry_count -eq 10 ] + then + echo "Health check failed ❌ port number: ${green_port}" + exit 1 + fi -nohup java -jar ${ABSDIR}/${APPNAME}.jar --spring.profiles.active=prod 1>> prod.log 2>> prod_error.log & # /home/ubuntu/ddang-0.0.1-SNAPSHOT.jar + echo "서버가 아직 실행되지 않았습니다... 시도 횟수: ${retry_count}" + sleep 10 +done + +echo "set \$service_port ${green_port};" | sudo tee /etc/nginx/conf.d/service-port.inc +echo "set \$actuator_port ${actuator_port};" | sudo tee /etc/nginx/conf.d/actuator-port.inc +sudo systemctl restart nginx + +echo "블루 서버를 종료합니다. port number: ${blue_port}" +fuser -s -k ${blue_port}/tcp + +if curl -s "http://localhost:${blue_port}" > /dev/null +then + echo "블루 서버가 아직 종료되지 않았습니다... 시도 횟수: ${blue_port}" + sleep 10 +else + echo "블루 서버가 종료되었습니다. port number: ${blue_port}" +fi diff --git a/backend/ddang/src/main/java/com/ddang/ddang/auction/application/AuctionService.java b/backend/ddang/src/main/java/com/ddang/ddang/auction/application/AuctionService.java index 725365c48..cc9164410 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/auction/application/AuctionService.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/auction/application/AuctionService.java @@ -7,38 +7,39 @@ import com.ddang.ddang.auction.application.exception.AuctionNotFoundException; import com.ddang.ddang.auction.application.exception.UserForbiddenException; import com.ddang.ddang.auction.domain.Auction; -import com.ddang.ddang.auction.infrastructure.persistence.JpaAuctionRepository; +import com.ddang.ddang.auction.domain.repository.AuctionRepository; import com.ddang.ddang.auction.presentation.dto.request.ReadAuctionSearchCondition; import com.ddang.ddang.category.application.exception.CategoryNotFoundException; import com.ddang.ddang.category.domain.Category; -import com.ddang.ddang.category.infrastructure.persistence.JpaCategoryRepository; +import com.ddang.ddang.category.domain.repository.CategoryRepository; import com.ddang.ddang.image.domain.AuctionImage; import com.ddang.ddang.image.domain.StoreImageProcessor; import com.ddang.ddang.image.domain.dto.StoreImageDto; import com.ddang.ddang.region.application.exception.RegionNotFoundException; import com.ddang.ddang.region.domain.AuctionRegion; import com.ddang.ddang.region.domain.Region; -import com.ddang.ddang.region.infrastructure.persistence.JpaRegionRepository; +import com.ddang.ddang.region.domain.repository.RegionRepository; import com.ddang.ddang.user.application.exception.UserNotFoundException; import com.ddang.ddang.user.domain.User; -import com.ddang.ddang.user.infrastructure.persistence.JpaUserRepository; -import java.time.LocalDateTime; -import java.util.List; +import com.ddang.ddang.user.domain.repository.UserRepository; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Slice; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.time.LocalDateTime; +import java.util.List; + @Service @Transactional(readOnly = true) @RequiredArgsConstructor public class AuctionService { - private final JpaUserRepository userRepository; - private final JpaAuctionRepository auctionRepository; - private final JpaRegionRepository regionRepository; - private final JpaCategoryRepository categoryRepository; + private final UserRepository userRepository; + private final AuctionRepository auctionRepository; + private final RegionRepository regionRepository; + private final CategoryRepository categoryRepository; private final StoreImageProcessor imageProcessor; @Transactional @@ -79,7 +80,7 @@ private void validateAuctionRegions(final List thirdRegions) { } public ReadAuctionDto readByAuctionId(final Long auctionId) { - final Auction findAuction = auctionRepository.findAuctionById(auctionId) + final Auction findAuction = auctionRepository.findTotalAuctionById(auctionId) .orElseThrow(() -> new AuctionNotFoundException( "지정한 아이디에 대한 경매를 찾을 수 없습니다." )); @@ -91,8 +92,8 @@ public ReadAuctionsDto readAllByCondition( final Pageable pageable, final ReadAuctionSearchCondition readAuctionSearchCondition) { final Slice auctions = auctionRepository.findAuctionsAllByCondition( - pageable, - readAuctionSearchCondition + readAuctionSearchCondition, + pageable ); return ReadAuctionsDto.of(auctions, LocalDateTime.now()); @@ -112,7 +113,7 @@ public ReadAuctionsDto readAllByBidderId(final Long userId, final Pageable pagea @Transactional public void deleteByAuctionId(final Long auctionId, final Long userId) { - final Auction auction = auctionRepository.findAuctionById(auctionId) + final Auction auction = auctionRepository.findTotalAuctionById(auctionId) .orElseThrow(() -> new AuctionNotFoundException( "지정한 아이디에 대한 경매를 찾을 수 없습니다." )); diff --git a/backend/ddang/src/main/java/com/ddang/ddang/auction/application/dto/ReadAuctionDto.java b/backend/ddang/src/main/java/com/ddang/ddang/auction/application/dto/ReadAuctionDto.java index 6f9677172..d5be76317 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/auction/application/dto/ReadAuctionDto.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/auction/application/dto/ReadAuctionDto.java @@ -29,7 +29,8 @@ public record ReadAuctionDto( String sellerName, double sellerReliability, boolean isSellerDeleted, - AuctionStatus auctionStatus + AuctionStatus auctionStatus, + Long lastBidderId ) { public static ReadAuctionDto of(final Auction auction, final LocalDateTime targetTime) { @@ -53,7 +54,8 @@ public static ReadAuctionDto of(final Auction auction, final LocalDateTime targe auction.getSeller().getName(), auction.getSeller().getReliability().getValue(), auction.getSeller().isDeleted(), - auction.findAuctionStatus(targetTime) + auction.findAuctionStatus(targetTime), + convertLastBidderId(auction) ); } @@ -79,4 +81,12 @@ private static List convertReadRegionsDto(final Auction auction) .map(ReadRegionsDto::from) .toList(); } + + private static Long convertLastBidderId(final Auction auction) { + if (auction.getLastBid() == null) { + return null; + } + + return auction.getLastBid().getBidder().getId(); + } } diff --git a/backend/ddang/src/main/java/com/ddang/ddang/auction/application/dto/ReadChatRoomDto.java b/backend/ddang/src/main/java/com/ddang/ddang/auction/application/dto/ReadChatRoomDto.java index 173ad3596..42125823c 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/auction/application/dto/ReadChatRoomDto.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/auction/application/dto/ReadChatRoomDto.java @@ -1,4 +1,6 @@ package com.ddang.ddang.auction.application.dto; public record ReadChatRoomDto(Long id, boolean isChatParticipant) { + + public static ReadChatRoomDto CANNOT_CHAT_DTO = new ReadChatRoomDto(null, false); } diff --git a/backend/ddang/src/main/java/com/ddang/ddang/auction/configuration/validator/ClosingTimeLimit.java b/backend/ddang/src/main/java/com/ddang/ddang/auction/configuration/validator/ClosingTimeLimit.java new file mode 100644 index 000000000..ad4b7e7b2 --- /dev/null +++ b/backend/ddang/src/main/java/com/ddang/ddang/auction/configuration/validator/ClosingTimeLimit.java @@ -0,0 +1,19 @@ +package com.ddang.ddang.auction.configuration.validator; + +import jakarta.validation.Constraint; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +@Constraint(validatedBy = ClosingTimeValidator.class) +public @interface ClosingTimeLimit { + + String message() default "마감 시간은 현재 일자로부터 최대 30일까지 설정할 수 있습니다."; + + Class[] groups() default {}; + + Class[] payload() default {}; +} diff --git a/backend/ddang/src/main/java/com/ddang/ddang/auction/configuration/validator/ClosingTimeValidator.java b/backend/ddang/src/main/java/com/ddang/ddang/auction/configuration/validator/ClosingTimeValidator.java new file mode 100644 index 000000000..f7dae24c7 --- /dev/null +++ b/backend/ddang/src/main/java/com/ddang/ddang/auction/configuration/validator/ClosingTimeValidator.java @@ -0,0 +1,22 @@ +package com.ddang.ddang.auction.configuration.validator; + +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; +import java.time.LocalDateTime; +import java.time.temporal.ChronoUnit; + +public class ClosingTimeValidator implements ConstraintValidator { + + private static final int MAXIMUM_CLOSING_TIME_DAYS = 30; + + @Override + public boolean isValid(final LocalDateTime target, final ConstraintValidatorContext context) { + if (target == null) { + return false; + } + + final long days = ChronoUnit.DAYS.between(LocalDateTime.now(), target); + + return days <= MAXIMUM_CLOSING_TIME_DAYS; + } +} diff --git a/backend/ddang/src/main/java/com/ddang/ddang/auction/infrastructure/persistence/dto/AuctionAndImageDto.java b/backend/ddang/src/main/java/com/ddang/ddang/auction/domain/dto/AuctionAndImageDto.java similarity index 73% rename from backend/ddang/src/main/java/com/ddang/ddang/auction/infrastructure/persistence/dto/AuctionAndImageDto.java rename to backend/ddang/src/main/java/com/ddang/ddang/auction/domain/dto/AuctionAndImageDto.java index 2615a219e..06fba43b8 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/auction/infrastructure/persistence/dto/AuctionAndImageDto.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/auction/domain/dto/AuctionAndImageDto.java @@ -1,4 +1,4 @@ -package com.ddang.ddang.auction.infrastructure.persistence.dto; +package com.ddang.ddang.auction.domain.dto; import com.ddang.ddang.auction.domain.Auction; import com.ddang.ddang.image.domain.AuctionImage; diff --git a/backend/ddang/src/main/java/com/ddang/ddang/auction/domain/repository/AuctionAndImageRepository.java b/backend/ddang/src/main/java/com/ddang/ddang/auction/domain/repository/AuctionAndImageRepository.java new file mode 100644 index 000000000..5ec07ba19 --- /dev/null +++ b/backend/ddang/src/main/java/com/ddang/ddang/auction/domain/repository/AuctionAndImageRepository.java @@ -0,0 +1,9 @@ +package com.ddang.ddang.auction.domain.repository; + +import com.ddang.ddang.auction.domain.dto.AuctionAndImageDto; +import java.util.Optional; + +public interface AuctionAndImageRepository { + + Optional findDtoByAuctionId(final Long auctionId); +} diff --git a/backend/ddang/src/main/java/com/ddang/ddang/auction/domain/repository/AuctionRepository.java b/backend/ddang/src/main/java/com/ddang/ddang/auction/domain/repository/AuctionRepository.java new file mode 100644 index 000000000..1d8be6dbc --- /dev/null +++ b/backend/ddang/src/main/java/com/ddang/ddang/auction/domain/repository/AuctionRepository.java @@ -0,0 +1,33 @@ +package com.ddang.ddang.auction.domain.repository; + +import com.ddang.ddang.auction.domain.Auction; +import com.ddang.ddang.auction.presentation.dto.request.ReadAuctionSearchCondition; + +import java.time.LocalDateTime; +import java.util.Optional; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Slice; + +public interface AuctionRepository { + + Auction save(final Auction auction); + + boolean existsById(final Long id); + + Optional findTotalAuctionById(final Long id); + + Optional findPureAuctionById(final Long id); + + Slice findAuctionsAllByCondition( + final ReadAuctionSearchCondition readAuctionSearchCondition, + final Pageable pageable + ); + + Slice findAuctionsAllByUserId(final Long userId, final Pageable pageable); + + Slice findAuctionsAllByBidderId(final Long bidderId, final Pageable pageable); + + boolean existsBySellerIdAndAuctionStatusIsOngoing(final Long userId, final LocalDateTime now); + + boolean existsLastBidByUserIdAndAuctionStatusIsOngoing(final Long userId, final LocalDateTime now); +} diff --git a/backend/ddang/src/main/java/com/ddang/ddang/auction/infrastructure/persistence/AuctionRepositoryImpl.java b/backend/ddang/src/main/java/com/ddang/ddang/auction/infrastructure/persistence/AuctionRepositoryImpl.java new file mode 100644 index 000000000..8064ae926 --- /dev/null +++ b/backend/ddang/src/main/java/com/ddang/ddang/auction/infrastructure/persistence/AuctionRepositoryImpl.java @@ -0,0 +1,71 @@ +package com.ddang.ddang.auction.infrastructure.persistence; + +import com.ddang.ddang.auction.domain.Auction; +import com.ddang.ddang.auction.domain.repository.AuctionRepository; +import com.ddang.ddang.auction.presentation.dto.request.ReadAuctionSearchCondition; + +import java.time.LocalDateTime; +import java.util.Optional; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Slice; +import org.springframework.stereotype.Repository; + +@Repository +@RequiredArgsConstructor +public class AuctionRepositoryImpl implements AuctionRepository { + + private final JpaAuctionRepository jpaAuctionRepository; + private final QuerydslAuctionRepository querydslAuctionRepository; + + @Override + public Auction save(final Auction auction) { + return jpaAuctionRepository.save(auction); + } + + @Override + public boolean existsById(final Long id) { + return jpaAuctionRepository.existsById(id); + } + + @Override + public Optional findTotalAuctionById(final Long id) { + return jpaAuctionRepository.findTotalAuctionById(id); + } + + @Override + public Optional findPureAuctionById(final Long id) { + return jpaAuctionRepository.findPureAuctionById(id); + } + + @Override + public Slice findAuctionsAllByCondition( + final ReadAuctionSearchCondition readAuctionSearchCondition, + final Pageable pageable + ) { + return querydslAuctionRepository.findAuctionsAllByCondition(readAuctionSearchCondition, pageable); + } + + @Override + public Slice findAuctionsAllByUserId(final Long userId, final Pageable pageable) { + return querydslAuctionRepository.findAuctionsAllByUserId(userId, pageable); + } + + @Override + public Slice findAuctionsAllByBidderId(final Long bidderId, final Pageable pageable) { + return querydslAuctionRepository.findAuctionsAllByBidderId(bidderId, pageable); + } + + @Override + public boolean existsBySellerIdAndAuctionStatusIsOngoing(final Long userId, final LocalDateTime now) { + return jpaAuctionRepository.existsBySellerIdAndDeletedIsFalseAndClosingTimeGreaterThanEqual(userId, now); + } + + @Override + public boolean existsLastBidByUserIdAndAuctionStatusIsOngoing(final Long userId, final LocalDateTime now) { + return jpaAuctionRepository.existsByLastBidBidderIdAndDeletedIsFalseAndClosingTimeGreaterThanEqual( + userId, + now + ); + } +} diff --git a/backend/ddang/src/main/java/com/ddang/ddang/auction/infrastructure/persistence/JpaAuctionRepository.java b/backend/ddang/src/main/java/com/ddang/ddang/auction/infrastructure/persistence/JpaAuctionRepository.java index bc6352f8c..8ccb8660f 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/auction/infrastructure/persistence/JpaAuctionRepository.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/auction/infrastructure/persistence/JpaAuctionRepository.java @@ -2,10 +2,38 @@ import com.ddang.ddang.auction.domain.Auction; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import java.time.LocalDateTime; import java.util.Optional; -public interface JpaAuctionRepository extends JpaRepository, QuerydslAuctionRepository, QuerydslAuctionAndImageRepository { +public interface JpaAuctionRepository extends JpaRepository { - Optional findByIdAndDeletedIsFalse(final Long id); + @Query(""" + SELECT a + FROM Auction a + LEFT JOIN FETCH a.auctionRegions ar + LEFT JOIN FETCh ar.thirdRegion tr + LEFT JOIN FETCH tr.firstRegion + LEFT JOIN FETCH tr.secondRegion + LEFT JOIN FETCH a.lastBid + JOIN FETCH a.subCategory sc + JOIN FETCH sc.mainCategory + JOIN FETCH a.seller + WHERE a.deleted = false AND a.id = :id + """) + Optional findTotalAuctionById(final Long id); + + @Query("SELECT a FROM Auction a WHERE a.deleted = false AND a.id = :id") + Optional findPureAuctionById(final Long id); + + boolean existsBySellerIdAndDeletedIsFalseAndClosingTimeGreaterThanEqual( + final Long userId, + final LocalDateTime now + ); + + boolean existsByLastBidBidderIdAndDeletedIsFalseAndClosingTimeGreaterThanEqual( + final Long userId, + final LocalDateTime now + ); } diff --git a/backend/ddang/src/main/java/com/ddang/ddang/auction/infrastructure/persistence/QuerydslAuctionAndImageRepository.java b/backend/ddang/src/main/java/com/ddang/ddang/auction/infrastructure/persistence/QuerydslAuctionAndImageRepository.java index 8c7c4fe4a..1b8915afc 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/auction/infrastructure/persistence/QuerydslAuctionAndImageRepository.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/auction/infrastructure/persistence/QuerydslAuctionAndImageRepository.java @@ -1,10 +1,46 @@ package com.ddang.ddang.auction.infrastructure.persistence; -import com.ddang.ddang.auction.infrastructure.persistence.dto.AuctionAndImageDto; +import static com.ddang.ddang.auction.domain.QAuction.auction; +import static com.ddang.ddang.bid.domain.QBid.bid; +import static com.ddang.ddang.image.domain.QAuctionImage.auctionImage; +import com.ddang.ddang.auction.domain.dto.AuctionAndImageDto; +import com.ddang.ddang.auction.domain.repository.AuctionAndImageRepository; +import com.ddang.ddang.auction.infrastructure.persistence.dto.AuctionAndImageQueryProjectionDto; +import com.ddang.ddang.auction.infrastructure.persistence.dto.QAuctionAndImageQueryProjectionDto; +import com.querydsl.jpa.JPAExpressions; +import com.querydsl.jpa.impl.JPAQueryFactory; import java.util.Optional; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Repository; -public interface QuerydslAuctionAndImageRepository { +@Repository +@RequiredArgsConstructor +public class QuerydslAuctionAndImageRepository implements AuctionAndImageRepository { - Optional findDtoByAuctionId(final Long auctionId); + private final JPAQueryFactory queryFactory; + + @Override + public Optional findDtoByAuctionId(final Long auctionId) { + final AuctionAndImageQueryProjectionDto auctionAndImageQueryProjectionDto = + queryFactory.select(new QAuctionAndImageQueryProjectionDto(auction, auctionImage)) + .from(auction) + .leftJoin(auction.lastBid, bid).fetchJoin() + .leftJoin(bid.bidder).fetchJoin() + .leftJoin(auctionImage).on(auctionImage.id.eq( + JPAExpressions + .select(auctionImage.id.min()) + .from(auctionImage) + .where(auctionImage.auction.id.eq(auction.id)) + .groupBy(auctionImage.auction.id) + )).fetchJoin() + .where(auction.id.eq(auctionId)) + .fetchOne(); + + if (auctionAndImageQueryProjectionDto == null) { + return Optional.empty(); + } + + return Optional.of(auctionAndImageQueryProjectionDto.toDto()); + } } diff --git a/backend/ddang/src/main/java/com/ddang/ddang/auction/infrastructure/persistence/QuerydslAuctionAndImageRepositoryImpl.java b/backend/ddang/src/main/java/com/ddang/ddang/auction/infrastructure/persistence/QuerydslAuctionAndImageRepositoryImpl.java deleted file mode 100644 index e1a1e79df..000000000 --- a/backend/ddang/src/main/java/com/ddang/ddang/auction/infrastructure/persistence/QuerydslAuctionAndImageRepositoryImpl.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.ddang.ddang.auction.infrastructure.persistence; - -import com.ddang.ddang.auction.infrastructure.persistence.dto.AuctionAndImageDto; -import com.ddang.ddang.auction.infrastructure.persistence.dto.AuctionAndImageQueryProjectionDto; -import com.ddang.ddang.auction.infrastructure.persistence.dto.QAuctionAndImageQueryProjectionDto; -import com.querydsl.jpa.JPAExpressions; -import com.querydsl.jpa.impl.JPAQueryFactory; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Repository; - -import java.util.Optional; - -import static com.ddang.ddang.auction.domain.QAuction.auction; -import static com.ddang.ddang.bid.domain.QBid.bid; -import static com.ddang.ddang.image.domain.QAuctionImage.auctionImage; - -@Repository -@RequiredArgsConstructor -public class QuerydslAuctionAndImageRepositoryImpl implements QuerydslAuctionAndImageRepository { - - private final JPAQueryFactory queryFactory; - - @Override - public Optional findDtoByAuctionId(final Long auctionId) { - final AuctionAndImageQueryProjectionDto auctionAndImageQueryProjectionDto = - queryFactory.select(new QAuctionAndImageQueryProjectionDto(auction, auctionImage)) - .from(auction) - .leftJoin(auction.lastBid, bid).fetchJoin() - .leftJoin(bid.bidder).fetchJoin() - .leftJoin(auctionImage).on(auctionImage.id.eq( - JPAExpressions - .select(auctionImage.id.min()) - .from(auctionImage) - .where(auctionImage.auction.id.eq(auction.id)) - .groupBy(auctionImage.auction.id) - )).fetchJoin() - .where(auction.id.eq(auctionId)) - .fetchOne(); - - if (auctionAndImageQueryProjectionDto == null) { - return Optional.empty(); - } - - return Optional.of(auctionAndImageQueryProjectionDto.toDto()); - } -} diff --git a/backend/ddang/src/main/java/com/ddang/ddang/auction/infrastructure/persistence/QuerydslAuctionRepository.java b/backend/ddang/src/main/java/com/ddang/ddang/auction/infrastructure/persistence/QuerydslAuctionRepository.java index 593ae4257..4ef4f2d15 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/auction/infrastructure/persistence/QuerydslAuctionRepository.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/auction/infrastructure/persistence/QuerydslAuctionRepository.java @@ -1,21 +1,191 @@ package com.ddang.ddang.auction.infrastructure.persistence; +import com.ddang.ddang.auction.configuration.util.AuctionSortConditionConsts; import com.ddang.ddang.auction.domain.Auction; +import com.ddang.ddang.auction.infrastructure.persistence.exception.UnsupportedSortConditionException; import com.ddang.ddang.auction.presentation.dto.request.ReadAuctionSearchCondition; -import java.util.Optional; +import com.ddang.ddang.common.helper.QuerydslSliceHelper; +import com.querydsl.core.types.OrderSpecifier; +import com.querydsl.core.types.dsl.BooleanExpression; +import com.querydsl.core.types.dsl.CaseBuilder; +import com.querydsl.jpa.impl.JPAQueryFactory; +import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Slice; +import org.springframework.data.domain.Sort; +import org.springframework.data.domain.Sort.Order; +import org.springframework.stereotype.Repository; -public interface QuerydslAuctionRepository { +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; - Slice findAuctionsAllByCondition( - final Pageable pageable, - final ReadAuctionSearchCondition readAuctionSearchCondition - ); +import static com.ddang.ddang.auction.domain.QAuction.auction; +import static com.ddang.ddang.bid.domain.QBid.bid; +import static com.ddang.ddang.category.domain.QCategory.category; +import static com.ddang.ddang.region.domain.QAuctionRegion.auctionRegion; +import static com.ddang.ddang.region.domain.QRegion.region; - Optional findAuctionById(final Long auctionId); +@Repository +@RequiredArgsConstructor +public class QuerydslAuctionRepository { - Slice findAuctionsAllByUserId(final Long userId, final Pageable pageable); + private static final long SLICE_OFFSET = 1L; + private static final int HIGH_PRIORITY = 2; + private static final int LOW_PRIORITY = 1; - Slice findAuctionsAllByBidderId(final Long bidderId, final Pageable pageable); + private final JPAQueryFactory queryFactory; + + public Slice findAuctionsAllByCondition( + final ReadAuctionSearchCondition readAuctionSearchCondition, + final Pageable pageable + ) { + final List> orderSpecifiers = calculateOrderSpecifiers(pageable); + final List booleanExpressions = calculateBooleanExpressions(readAuctionSearchCondition); + final List findAuctionIds = findAuctionIds(booleanExpressions, orderSpecifiers, pageable); + final List findAuctions = findAuctionsByIdsAndOrderSpecifiers(findAuctionIds, orderSpecifiers); + + return QuerydslSliceHelper.toSlice(findAuctions, pageable); + } + + private List> calculateOrderSpecifiers(final Pageable pageable) { + final List> orderSpecifiers = new ArrayList<>(); + + orderSpecifiers.add(closingTimeOrderSpecifier()); + orderSpecifiers.addAll(processOrderSpecifiers(pageable)); + orderSpecifiers.add(auction.id.desc()); + + return orderSpecifiers; + } + + private List> processOrderSpecifiers(final Pageable pageable) { + final List> orderSpecifiers = new ArrayList<>(); + final Sort sort = pageable.getSort(); + + for (final Order order : sort) { + if (AuctionSortConditionConsts.ID.equals(order.getProperty())) { + return Collections.emptyList(); + } + + orderSpecifiers.add(processOrderSpecifierByCondition(order)); + } + + return orderSpecifiers; + } + + private OrderSpecifier processOrderSpecifierByCondition(final Order order) { + if (AuctionSortConditionConsts.RELIABILITY.equals(order.getProperty())) { + return auction.seller.reliability.value.desc(); + } + if (AuctionSortConditionConsts.AUCTIONEER_COUNT.equals(order.getProperty())) { + return auction.auctioneerCount.desc(); + } + if (AuctionSortConditionConsts.CLOSING_TINE.equals(order.getProperty())) { + return auction.closingTime.asc(); + } + + throw new UnsupportedSortConditionException("지원하지 않는 정렬 방식입니다."); + } + + private OrderSpecifier closingTimeOrderSpecifier() { + final LocalDateTime now = LocalDateTime.now(); + + return new CaseBuilder() + .when(auction.closingTime.after(now)).then(LOW_PRIORITY) + .otherwise(HIGH_PRIORITY) + .asc(); + } + + private List calculateBooleanExpressions(final ReadAuctionSearchCondition searchCondition) { + final List booleanExpressions = new ArrayList<>(); + + booleanExpressions.add(auction.deleted.isFalse()); + + final BooleanExpression titleBooleanExpression = convertTitleSearchCondition(searchCondition); + + if (titleBooleanExpression != null) { + booleanExpressions.add(titleBooleanExpression); + } + + return booleanExpressions; + } + + private List findAuctionIds( + final List booleanExpressions, + final List> orderSpecifiers, + final Pageable pageable + ) { + return queryFactory.select(auction.id) + .from(auction) + .where(booleanExpressions.toArray(BooleanExpression[]::new)) + .orderBy(orderSpecifiers.toArray(OrderSpecifier[]::new)) + .limit(pageable.getPageSize() + SLICE_OFFSET) + .offset(pageable.getOffset()) + .fetch(); + } + + private BooleanExpression convertTitleSearchCondition(final ReadAuctionSearchCondition readAuctionSearchCondition) { + final String titleSearchCondition = readAuctionSearchCondition.title(); + + if (titleSearchCondition == null) { + return null; + } + + return auction.title.like("%" + titleSearchCondition + "%"); + } + + private List findAuctionsByIdsAndOrderSpecifiers( + final List targetIds, + final List> orderSpecifiers + ) { + return queryFactory.selectFrom(auction) + .leftJoin(auction.auctionRegions, auctionRegion).fetchJoin() + .leftJoin(auctionRegion.thirdRegion, region).fetchJoin() + .leftJoin(region.firstRegion).fetchJoin() + .leftJoin(region.secondRegion).fetchJoin() + .leftJoin(auction.lastBid).fetchJoin() + .join(auction.subCategory, category).fetchJoin() + .join(category.mainCategory).fetchJoin() + .join(auction.seller).fetchJoin() + .where(auction.id.in(targetIds.toArray(Long[]::new))) + .orderBy(orderSpecifiers.toArray(OrderSpecifier[]::new)) + .fetch(); + } + + public Slice findAuctionsAllByUserId(final Long userId, final Pageable pageable) { + final List booleanExpressions = List.of( + auction.seller.id.eq(userId), + auction.deleted.isFalse() + ); + final List> orderSpecifiers = List.of(auction.id.desc()); + final List findAuctionIds = findAuctionIds(booleanExpressions, orderSpecifiers, pageable); + final List findAuctions = findAuctionsByIdsAndOrderSpecifiers( + findAuctionIds, + List.of(auction.id.desc()) + ); + + return QuerydslSliceHelper.toSlice(findAuctions, pageable); + } + + public Slice findAuctionsAllByBidderId(final Long bidderId, final Pageable pageable) { + final List findAuctionIds = queryFactory.select(bid.auction.id) + .from(bid) + .where(bid.bidder.id.eq(bidderId)) + .groupBy(bid.auction.id) + .orderBy(bid.id.max().desc()) + .limit(pageable.getPageSize() + SLICE_OFFSET) + .offset(pageable.getOffset()) + .fetch(); + final List findAuctions = findAuctionsByIdsAndOrderSpecifiers(findAuctionIds, Collections.emptyList()); + + findAuctions.sort((firstAuction, secondAuction) -> { + int firstAuctionIndex = findAuctionIds.indexOf(firstAuction.getId()); + int secondAuctionIndex = findAuctionIds.indexOf(secondAuction.getId()); + + return Integer.compare(firstAuctionIndex, secondAuctionIndex); + }); + + return QuerydslSliceHelper.toSlice(findAuctions, pageable); + } } diff --git a/backend/ddang/src/main/java/com/ddang/ddang/auction/infrastructure/persistence/QuerydslAuctionRepositoryImpl.java b/backend/ddang/src/main/java/com/ddang/ddang/auction/infrastructure/persistence/QuerydslAuctionRepositoryImpl.java deleted file mode 100644 index d6a9aa6e0..000000000 --- a/backend/ddang/src/main/java/com/ddang/ddang/auction/infrastructure/persistence/QuerydslAuctionRepositoryImpl.java +++ /dev/null @@ -1,209 +0,0 @@ -package com.ddang.ddang.auction.infrastructure.persistence; - -import static com.ddang.ddang.auction.domain.QAuction.auction; -import static com.ddang.ddang.bid.domain.QBid.bid; -import static com.ddang.ddang.category.domain.QCategory.category; -import static com.ddang.ddang.region.domain.QAuctionRegion.auctionRegion; -import static com.ddang.ddang.region.domain.QRegion.region; - -import com.ddang.ddang.auction.configuration.util.AuctionSortConditionConsts; -import com.ddang.ddang.auction.domain.Auction; -import com.ddang.ddang.auction.infrastructure.persistence.exception.UnsupportedSortConditionException; -import com.ddang.ddang.auction.presentation.dto.request.ReadAuctionSearchCondition; -import com.ddang.ddang.common.helper.QuerydslSliceHelper; -import com.querydsl.core.types.OrderSpecifier; -import com.querydsl.core.types.dsl.BooleanExpression; -import com.querydsl.core.types.dsl.CaseBuilder; -import com.querydsl.jpa.impl.JPAQueryFactory; -import java.time.LocalDateTime; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Optional; -import lombok.RequiredArgsConstructor; -import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Slice; -import org.springframework.data.domain.Sort; -import org.springframework.data.domain.Sort.Order; -import org.springframework.stereotype.Repository; - -@Repository -@RequiredArgsConstructor -public class QuerydslAuctionRepositoryImpl implements QuerydslAuctionRepository { - - private static final long SLICE_OFFSET = 1L; - private static final int HIGH_PRIORITY = 2; - private static final int LOW_PRIORITY = 1; - - private final JPAQueryFactory queryFactory; - - @Override - public Slice findAuctionsAllByCondition( - final Pageable pageable, - final ReadAuctionSearchCondition readAuctionSearchCondition - ) { - final List> orderSpecifiers = calculateOrderSpecifiers(pageable); - final List booleanExpressions = calculateBooleanExpressions(readAuctionSearchCondition); - final List findAuctionIds = findAuctionIds(booleanExpressions, orderSpecifiers, pageable); - final List findAuctions = findAuctionsByIdsAndOrderSpecifiers(findAuctionIds, orderSpecifiers); - - return QuerydslSliceHelper.toSlice(findAuctions, pageable); - } - - private List> calculateOrderSpecifiers(final Pageable pageable) { - final List> orderSpecifiers = new ArrayList<>(processOrderSpecifiers(pageable)); - - orderSpecifiers.add(auction.id.desc()); - - return orderSpecifiers; - } - - private List> processOrderSpecifiers(final Pageable pageable) { - final List> orderSpecifiers = new ArrayList<>(); - final Sort sort = pageable.getSort(); - - for (final Order order : sort) { - if (AuctionSortConditionConsts.ID.equals(order.getProperty())) { - return Collections.emptyList(); - } - - orderSpecifiers.addAll(processOrderSpecifierByCondition(order)); - } - - return orderSpecifiers; - } - - private List> processOrderSpecifierByCondition(final Order order) { - if (AuctionSortConditionConsts.RELIABILITY.equals(order.getProperty())) { - return List.of(closingTimeOrderSpecifier(), auction.seller.reliability.value.desc()); - } - if (AuctionSortConditionConsts.AUCTIONEER_COUNT.equals(order.getProperty())) { - return List.of(closingTimeOrderSpecifier(), auction.auctioneerCount.desc()); - } - if (AuctionSortConditionConsts.CLOSING_TINE.equals(order.getProperty())) { - return List.of(closingTimeOrderSpecifier(), auction.closingTime.asc()); - } - - throw new UnsupportedSortConditionException("지원하지 않는 정렬 방식입니다."); - } - - private OrderSpecifier closingTimeOrderSpecifier() { - final LocalDateTime now = LocalDateTime.now(); - - return new CaseBuilder() - .when(auction.closingTime.after(now)).then(LOW_PRIORITY) - .otherwise(HIGH_PRIORITY) - .asc(); - } - - private List calculateBooleanExpressions(final ReadAuctionSearchCondition searchCondition) { - final List booleanExpressions = new ArrayList<>(); - - booleanExpressions.add(auction.deleted.isFalse()); - - final BooleanExpression titleBooleanExpression = convertTitleSearchCondition(searchCondition); - - if (titleBooleanExpression != null) { - booleanExpressions.add(titleBooleanExpression); - } - - return booleanExpressions; - } - - private List findAuctionIds( - final List booleanExpressions, - final List> orderSpecifiers, - final Pageable pageable - ) { - return queryFactory.select(auction.id) - .from(auction) - .where(booleanExpressions.toArray(BooleanExpression[]::new)) - .orderBy(orderSpecifiers.toArray(OrderSpecifier[]::new)) - .limit(pageable.getPageSize() + SLICE_OFFSET) - .offset(pageable.getOffset()) - .fetch(); - } - - private BooleanExpression convertTitleSearchCondition(final ReadAuctionSearchCondition readAuctionSearchCondition) { - final String titleSearchCondition = readAuctionSearchCondition.title(); - - if (titleSearchCondition == null) { - return null; - } - - return auction.title.like("%" + titleSearchCondition + "%"); - } - - private List findAuctionsByIdsAndOrderSpecifiers( - final List targetIds, - final List> orderSpecifiers - ) { - return queryFactory.selectFrom(auction) - .leftJoin(auction.auctionRegions, auctionRegion).fetchJoin() - .leftJoin(auctionRegion.thirdRegion, region).fetchJoin() - .leftJoin(region.firstRegion).fetchJoin() - .leftJoin(region.secondRegion).fetchJoin() - .leftJoin(auction.subCategory, category).fetchJoin() - .leftJoin(category.mainCategory).fetchJoin() - .leftJoin(auction.seller).fetchJoin() - .leftJoin(auction.lastBid).fetchJoin() - .where(auction.id.in(targetIds.toArray(Long[]::new))) - .orderBy(orderSpecifiers.toArray(OrderSpecifier[]::new)) - .fetch(); - } - - @Override - public Optional findAuctionById(final Long auctionId) { - final Auction findAuction = queryFactory.selectFrom(auction) - .leftJoin(auction.auctionRegions, auctionRegion).fetchJoin() - .leftJoin(auctionRegion.thirdRegion, region).fetchJoin() - .leftJoin(region.firstRegion).fetchJoin() - .leftJoin(region.secondRegion).fetchJoin() - .leftJoin(auction.subCategory, category).fetchJoin() - .leftJoin(category.mainCategory).fetchJoin() - .leftJoin(auction.seller).fetchJoin() - .leftJoin(auction.lastBid).fetchJoin() - .where(auction.deleted.isFalse(), auction.id.eq(auctionId)) - .fetchOne(); - - return Optional.ofNullable(findAuction); - } - - @Override - public Slice findAuctionsAllByUserId(final Long userId, final Pageable pageable) { - final List booleanExpressions = List.of( - auction.seller.id.eq(userId), - auction.deleted.isFalse() - ); - final List> orderSpecifiers = List.of(auction.id.desc()); - final List findAuctionIds = findAuctionIds(booleanExpressions, orderSpecifiers, pageable); - final List findAuctions = findAuctionsByIdsAndOrderSpecifiers( - findAuctionIds, - List.of(auction.id.desc()) - ); - - return QuerydslSliceHelper.toSlice(findAuctions, pageable); - } - - @Override - public Slice findAuctionsAllByBidderId(final Long bidderId, final Pageable pageable) { - final List findAuctionIds = queryFactory.select(bid.auction.id) - .from(bid) - .where(bid.bidder.id.eq(bidderId)) - .groupBy(bid.auction.id) - .orderBy(bid.id.max().desc()) - .limit(pageable.getPageSize() + SLICE_OFFSET) - .offset(pageable.getOffset()) - .fetch(); - final List findAuctions = findAuctionsByIdsAndOrderSpecifiers(findAuctionIds, Collections.emptyList()); - - findAuctions.sort((firstAuction, secondAuction) -> { - int firstAuctionIndex = findAuctionIds.indexOf(firstAuction.getId()); - int secondAuctionIndex = findAuctionIds.indexOf(secondAuction.getId()); - - return Integer.compare(firstAuctionIndex, secondAuctionIndex); - }); - - return QuerydslSliceHelper.toSlice(findAuctions, pageable); - } -} diff --git a/backend/ddang/src/main/java/com/ddang/ddang/auction/infrastructure/persistence/dto/AuctionAndImageQueryProjectionDto.java b/backend/ddang/src/main/java/com/ddang/ddang/auction/infrastructure/persistence/dto/AuctionAndImageQueryProjectionDto.java index 85300e897..909372950 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/auction/infrastructure/persistence/dto/AuctionAndImageQueryProjectionDto.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/auction/infrastructure/persistence/dto/AuctionAndImageQueryProjectionDto.java @@ -1,6 +1,7 @@ package com.ddang.ddang.auction.infrastructure.persistence.dto; import com.ddang.ddang.auction.domain.Auction; +import com.ddang.ddang.auction.domain.dto.AuctionAndImageDto; import com.ddang.ddang.image.domain.AuctionImage; import com.querydsl.core.annotations.QueryProjection; @@ -10,7 +11,6 @@ public record AuctionAndImageQueryProjectionDto(Auction auction, AuctionImage au public AuctionAndImageQueryProjectionDto { } - // TODO: 2023/09/22 dto이름 정해지면 명확한 dto이름으로 바꾸기 public AuctionAndImageDto toDto() { return new AuctionAndImageDto(this.auction, this.auctionImage); } diff --git a/backend/ddang/src/main/java/com/ddang/ddang/auction/presentation/AuctionController.java b/backend/ddang/src/main/java/com/ddang/ddang/auction/presentation/AuctionController.java index fa3dad2fd..fa69bc062 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/auction/presentation/AuctionController.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/auction/presentation/AuctionController.java @@ -16,8 +16,6 @@ import com.ddang.ddang.authentication.domain.dto.AuthenticationUserInfo; import com.ddang.ddang.chat.application.ChatRoomService; import jakarta.validation.Valid; -import java.net.URI; -import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Pageable; import org.springframework.http.ResponseEntity; @@ -29,7 +27,9 @@ import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; -import org.springframework.web.servlet.support.ServletUriComponentsBuilder; + +import java.net.URI; +import java.util.List; @RestController @RequestMapping("/auctions") diff --git a/backend/ddang/src/main/java/com/ddang/ddang/auction/presentation/AuctionQnaController.java b/backend/ddang/src/main/java/com/ddang/ddang/auction/presentation/AuctionQnaController.java index 42f9da78f..9a9c4e12d 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/auction/presentation/AuctionQnaController.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/auction/presentation/AuctionQnaController.java @@ -1,6 +1,8 @@ package com.ddang.ddang.auction.presentation; import com.ddang.ddang.auction.presentation.dto.response.ReadQnasResponse; +import com.ddang.ddang.authentication.configuration.AuthenticateUser; +import com.ddang.ddang.authentication.domain.dto.AuthenticationUserInfo; import com.ddang.ddang.qna.application.QuestionService; import com.ddang.ddang.qna.application.dto.ReadQnasDto; import lombok.RequiredArgsConstructor; @@ -18,8 +20,11 @@ public class AuctionQnaController { private final QuestionService questionService; @GetMapping("/{auctionId}/questions") - public ResponseEntity readAllByAuctionId(@PathVariable final Long auctionId) { - final ReadQnasDto readQnasDto = questionService.readAllByAuctionId(auctionId); + public ResponseEntity readAllByAuctionId( + @AuthenticateUser AuthenticationUserInfo userInfo, + @PathVariable final Long auctionId + ) { + final ReadQnasDto readQnasDto = questionService.readAllByAuctionId(auctionId, userInfo.userId()); final ReadQnasResponse response = ReadQnasResponse.from(readQnasDto); return ResponseEntity.ok(response); diff --git a/backend/ddang/src/main/java/com/ddang/ddang/auction/presentation/dto/request/CreateAuctionRequest.java b/backend/ddang/src/main/java/com/ddang/ddang/auction/presentation/dto/request/CreateAuctionRequest.java index 753fff9b5..09c891242 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/auction/presentation/dto/request/CreateAuctionRequest.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/auction/presentation/dto/request/CreateAuctionRequest.java @@ -1,10 +1,10 @@ package com.ddang.ddang.auction.presentation.dto.request; -import jakarta.validation.constraints.FutureOrPresent; +import com.ddang.ddang.auction.configuration.validator.ClosingTimeLimit; +import jakarta.validation.constraints.Future; import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Positive; - import java.time.LocalDateTime; import java.util.List; @@ -24,7 +24,8 @@ public record CreateAuctionRequest( Integer startPrice, @NotNull(message = "마감 시간이 입력되지 않았습니다.") - @FutureOrPresent(message = "마감 시간은 과거를 입력할 수 없습니다.") + @Future(message = "마감 시간은 과거를 입력할 수 없습니다.") + @ClosingTimeLimit LocalDateTime closingTime, @NotNull(message = "하위 카테고리가 입력되지 않았습니다.") diff --git a/backend/ddang/src/main/java/com/ddang/ddang/auction/presentation/dto/response/ReadAuctionDetailResponse.java b/backend/ddang/src/main/java/com/ddang/ddang/auction/presentation/dto/response/ReadAuctionDetailResponse.java index 318daccce..3baa08f29 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/auction/presentation/dto/response/ReadAuctionDetailResponse.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/auction/presentation/dto/response/ReadAuctionDetailResponse.java @@ -8,7 +8,8 @@ public record ReadAuctionDetailResponse( AuctionDetailResponse auction, SellerResponse seller, ChatRoomInAuctionResponse chat, - boolean isOwner + boolean isOwner, + boolean isLastBidder ) { public static ReadAuctionDetailResponse of( @@ -24,11 +25,16 @@ public static ReadAuctionDetailResponse of( auctionDetailResponse, sellerResponse, chatRoomResponse, - isOwner(auctionDto, userInfo) + isOwner(auctionDto, userInfo), + isLastBidder(auctionDto, userInfo) ); } private static boolean isOwner(final ReadAuctionDto auctionDto, final AuthenticationUserInfo userInfo) { return auctionDto.sellerId().equals(userInfo.userId()); } + + private static boolean isLastBidder(final ReadAuctionDto auctionDto, final AuthenticationUserInfo userInfo) { + return userInfo.userId().equals(auctionDto.lastBidderId()); + } } diff --git a/backend/ddang/src/main/java/com/ddang/ddang/auction/presentation/dto/response/ReadAuctionResponse.java b/backend/ddang/src/main/java/com/ddang/ddang/auction/presentation/dto/response/ReadAuctionResponse.java index a11aaaf06..4eee887ff 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/auction/presentation/dto/response/ReadAuctionResponse.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/auction/presentation/dto/response/ReadAuctionResponse.java @@ -4,7 +4,6 @@ import com.ddang.ddang.image.presentation.util.ImageRelativeUrl; import com.ddang.ddang.image.presentation.util.ImageUrlCalculator; -// TODO: 9/29/23 추후 대표 이미지 관련 필드 추가 public record ReadAuctionResponse( Long id, String title, diff --git a/backend/ddang/src/main/java/com/ddang/ddang/auction/presentation/dto/response/ReadQnaResponse.java b/backend/ddang/src/main/java/com/ddang/ddang/auction/presentation/dto/response/ReadQnaResponse.java index dd71a5eca..25a6ae9a1 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/auction/presentation/dto/response/ReadQnaResponse.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/auction/presentation/dto/response/ReadQnaResponse.java @@ -16,7 +16,7 @@ public static ReadQnaResponse from(final ReadQnaDto readQnaDto) { } private static ReadAnswerResponse processReadAnswerResponse(final ReadAnswerDto readAnswerDto) { - if (readAnswerDto == null) { + if (readAnswerDto == null || readAnswerDto.isDeleted()) { return null; } diff --git a/backend/ddang/src/main/java/com/ddang/ddang/auction/presentation/dto/response/ReadQuestionResponse.java b/backend/ddang/src/main/java/com/ddang/ddang/auction/presentation/dto/response/ReadQuestionResponse.java index 9a3305a8a..efc17e944 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/auction/presentation/dto/response/ReadQuestionResponse.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/auction/presentation/dto/response/ReadQuestionResponse.java @@ -13,7 +13,9 @@ public record ReadQuestionResponse( @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss", timezone = "Asia/Seoul") LocalDateTime createdTime, - String content + String content, + + boolean isQuestioner ) { public static ReadQuestionResponse from(final ReadQuestionDto readQuestionDto) { @@ -21,7 +23,8 @@ public static ReadQuestionResponse from(final ReadQuestionDto readQuestionDto) { readQuestionDto.id(), ReadUserInAuctionQuestionResponse.from(readQuestionDto.readUserInQnaDto()), readQuestionDto.createdTime(), - readQuestionDto.content() + readQuestionDto.content(), + readQuestionDto.isQuestioner() ); } } diff --git a/backend/ddang/src/main/java/com/ddang/ddang/auction/presentation/dto/response/ReadReviewDetailResponse.java b/backend/ddang/src/main/java/com/ddang/ddang/auction/presentation/dto/response/ReadReviewDetailResponse.java index b50f0e3f1..85aee0385 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/auction/presentation/dto/response/ReadReviewDetailResponse.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/auction/presentation/dto/response/ReadReviewDetailResponse.java @@ -7,7 +7,7 @@ public record ReadReviewDetailResponse(@Nullable Float score, @Nullable String c public static ReadReviewDetailResponse from(final ReadReviewDetailDto readReviewDetailDto) { final Double nullableScore = readReviewDetailDto.score(); - if(nullableScore == null) { + if (nullableScore == null) { return new ReadReviewDetailResponse(null, readReviewDetailDto.content()); } diff --git a/backend/ddang/src/main/java/com/ddang/ddang/auction/presentation/dto/response/ReadUserInAuctionQuestionResponse.java b/backend/ddang/src/main/java/com/ddang/ddang/auction/presentation/dto/response/ReadUserInAuctionQuestionResponse.java index b9f3d7051..336306eb2 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/auction/presentation/dto/response/ReadUserInAuctionQuestionResponse.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/auction/presentation/dto/response/ReadUserInAuctionQuestionResponse.java @@ -3,15 +3,14 @@ import com.ddang.ddang.image.presentation.util.ImageRelativeUrl; import com.ddang.ddang.image.presentation.util.ImageUrlCalculator; import com.ddang.ddang.qna.application.dto.ReadUserInQnaDto; +import com.ddang.ddang.user.presentation.util.NameProcessor; public record ReadUserInAuctionQuestionResponse(Long id, String name, String image) { - public static ReadUserInAuctionQuestionResponse from( - final ReadUserInQnaDto writerDto - ) { + public static ReadUserInAuctionQuestionResponse from(final ReadUserInQnaDto writerDto) { return new ReadUserInAuctionQuestionResponse( writerDto.id(), - writerDto.name(), + NameProcessor.process(writerDto.isDeleted(), writerDto.name()), ImageUrlCalculator.calculateBy(ImageRelativeUrl.USER, writerDto.profileImageId()) ); } diff --git a/backend/ddang/src/main/java/com/ddang/ddang/auction/presentation/dto/response/SellerResponse.java b/backend/ddang/src/main/java/com/ddang/ddang/auction/presentation/dto/response/SellerResponse.java index 37ebc7804..994fa91ac 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/auction/presentation/dto/response/SellerResponse.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/auction/presentation/dto/response/SellerResponse.java @@ -3,6 +3,7 @@ import com.ddang.ddang.auction.application.dto.ReadAuctionDto; import com.ddang.ddang.image.presentation.util.ImageRelativeUrl; import com.ddang.ddang.image.presentation.util.ImageUrlCalculator; +import com.ddang.ddang.user.presentation.util.NameProcessor; public record SellerResponse( Long id, @@ -17,7 +18,7 @@ public static SellerResponse from(final ReadAuctionDto auctionDto) { return new SellerResponse( auctionDto.sellerId(), ImageUrlCalculator.calculateBy(ImageRelativeUrl.USER, auctionDto.sellerProfileId()), - auctionDto.sellerName(), + NameProcessor.process(auctionDto.isSellerDeleted(), auctionDto.sellerName()), floatReliability ); } diff --git a/backend/ddang/src/main/java/com/ddang/ddang/authentication/application/AuthenticationService.java b/backend/ddang/src/main/java/com/ddang/ddang/authentication/application/AuthenticationService.java index 58add0567..21b8dee8b 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/authentication/application/AuthenticationService.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/authentication/application/AuthenticationService.java @@ -1,7 +1,11 @@ package com.ddang.ddang.authentication.application; +import com.ddang.ddang.auction.domain.repository.AuctionRepository; +import com.ddang.ddang.authentication.application.dto.LoginInformationDto; +import com.ddang.ddang.authentication.application.dto.LoginUserInformationDto; import com.ddang.ddang.authentication.application.dto.TokenDto; import com.ddang.ddang.authentication.application.exception.InvalidWithdrawalException; +import com.ddang.ddang.authentication.application.exception.WithdrawalNotAllowedException; import com.ddang.ddang.authentication.application.util.RandomNameGenerator; import com.ddang.ddang.authentication.domain.Oauth2UserInformationProviderComposite; import com.ddang.ddang.authentication.domain.TokenDecoder; @@ -14,71 +18,78 @@ import com.ddang.ddang.authentication.infrastructure.oauth2.Oauth2Type; import com.ddang.ddang.device.application.DeviceTokenService; import com.ddang.ddang.device.application.dto.PersistDeviceTokenDto; -import com.ddang.ddang.device.infrastructure.persistence.JpaDeviceTokenRepository; -import com.ddang.ddang.image.application.exception.ImageNotFoundException; -import com.ddang.ddang.image.domain.ProfileImage; -import com.ddang.ddang.image.infrastructure.persistence.JpaProfileImageRepository; +import com.ddang.ddang.device.domain.repository.DeviceTokenRepository; import com.ddang.ddang.user.domain.Reliability; import com.ddang.ddang.user.domain.User; -import com.ddang.ddang.user.infrastructure.persistence.JpaUserRepository; +import com.ddang.ddang.user.domain.repository.UserRepository; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.time.LocalDateTime; import java.util.Map; - -import static com.ddang.ddang.image.domain.ProfileImage.DEFAULT_PROFILE_IMAGE_STORE_NAME; +import java.util.concurrent.atomic.AtomicBoolean; @Service @Transactional(readOnly = true) @RequiredArgsConstructor public class AuthenticationService { + private static final Reliability INITIALIZE_USER_RELIABILITY = new Reliability(0.0d); private static final String PRIVATE_CLAIMS_KEY = "userId"; private final DeviceTokenService deviceTokenService; private final Oauth2UserInformationProviderComposite providerComposite; - private final JpaUserRepository userRepository; - private final JpaProfileImageRepository profileImageRepository; + private final UserRepository userRepository; + private final AuctionRepository auctionRepository; private final TokenEncoder tokenEncoder; private final TokenDecoder tokenDecoder; private final BlackListTokenService blackListTokenService; - private final JpaDeviceTokenRepository deviceTokenRepository; + private final DeviceTokenRepository deviceTokenRepository; @Transactional - public TokenDto login(final Oauth2Type oauth2Type, final String oauth2AccessToken, final String deviceToken) { + public LoginInformationDto login( + final Oauth2Type oauth2Type, + final String oauth2AccessToken, + final String deviceToken + ) { final OAuth2UserInformationProvider provider = providerComposite.findProvider(oauth2Type); final UserInformationDto userInformationDto = provider.findUserInformation(oauth2AccessToken); - final User persistUser = findOrPersistUser(oauth2Type, userInformationDto); + final LoginUserInformationDto loginUserInfo = findOrPersistUser(oauth2Type, userInformationDto); - updateOrPersistDeviceToken(deviceToken, persistUser); + updateOrPersistDeviceToken(deviceToken, loginUserInfo.user()); - return convertTokenDto(persistUser); + return LoginInformationDto.of(convertTokenDto(loginUserInfo), loginUserInfo); } private void updateOrPersistDeviceToken(final String deviceToken, final User persistUser) { final PersistDeviceTokenDto persistDeviceTokenDto = new PersistDeviceTokenDto(deviceToken); - deviceTokenService.persist(persistUser.getId(), persistDeviceTokenDto); - } - private User findOrPersistUser(final Oauth2Type oauth2Type, final UserInformationDto userInformationDto) { - return userRepository.findByOauthIdAndDeletedIsFalse(userInformationDto.findUserId()) - .orElseGet(() -> { - final User user = User.builder() - .name(oauth2Type.calculateNickname(calculateRandomNumber())) - .profileImage(findDefaultProfileImage()) - .reliability(new Reliability(0.0d)) - .oauthId(userInformationDto.findUserId()) - .build(); - - return userRepository.save(user); - }); + deviceTokenService.persist(persistUser.getId(), persistDeviceTokenDto); } - private ProfileImage findDefaultProfileImage() { - return profileImageRepository.findByStoreName(DEFAULT_PROFILE_IMAGE_STORE_NAME) - .orElseThrow(() -> new ImageNotFoundException("기본 이미지를 찾을 수 없습니다.")); + private LoginUserInformationDto findOrPersistUser( + final Oauth2Type oauth2Type, + final UserInformationDto userInformationDto + ) { + final AtomicBoolean isSignUpUser = new AtomicBoolean(false); + + final User signInUser = userRepository.findByOauthId(userInformationDto.findUserId()) + .orElseGet(() -> { + final User user = User.builder() + .name(oauth2Type.calculateNickname( + calculateRandomNumber()) + ) + .reliability(INITIALIZE_USER_RELIABILITY) + .oauthId(userInformationDto.findUserId()) + .oauth2Type(oauth2Type) + .build(); + + isSignUpUser.set(true); + return userRepository.save(user); + }); + + return new LoginUserInformationDto(signInUser, isSignUpUser.get()); } private String calculateRandomNumber() { @@ -95,16 +106,18 @@ private boolean isAlreadyExist(final String name) { return userRepository.existsByNameEndingWith(name); } - private TokenDto convertTokenDto(final User persistUser) { + private TokenDto convertTokenDto(final LoginUserInformationDto signInUserInfo) { + final User loginUser = signInUserInfo.user(); + final String accessToken = tokenEncoder.encode( LocalDateTime.now(), TokenType.ACCESS, - Map.of(PRIVATE_CLAIMS_KEY, persistUser.getId()) + Map.of(PRIVATE_CLAIMS_KEY, loginUser.getId()) ); final String refreshToken = tokenEncoder.encode( LocalDateTime.now(), TokenType.REFRESH, - Map.of(PRIVATE_CLAIMS_KEY, persistUser.getId()) + Map.of(PRIVATE_CLAIMS_KEY, loginUser.getId()) ); return new TokenDto(accessToken, refreshToken); @@ -121,7 +134,13 @@ public TokenDto refreshToken(final String refreshToken) { Map.of(PRIVATE_CLAIMS_KEY, privateClaims.userId()) ); - return new TokenDto(accessToken, refreshToken); + final String newRefreshToken = tokenEncoder.encode( + LocalDateTime.now(), + TokenType.REFRESH, + Map.of(PRIVATE_CLAIMS_KEY, privateClaims.userId()) + ); + + return new TokenDto(accessToken, newRefreshToken); } public boolean validateToken(final String accessToken) { @@ -131,21 +150,32 @@ public boolean validateToken(final String accessToken) { @Transactional public void withdrawal( - final Oauth2Type oauth2Type, final String accessToken, final String refreshToken ) throws InvalidWithdrawalException { - final OAuth2UserInformationProvider provider = providerComposite.findProvider(oauth2Type); final PrivateClaims privateClaims = tokenDecoder.decode(TokenType.ACCESS, accessToken) - .orElseThrow(() -> - new InvalidTokenException("유효한 토큰이 아닙니다.") - ); - final User user = userRepository.findByIdAndDeletedIsFalse(privateClaims.userId()) - .orElseThrow(() -> new InvalidWithdrawalException("탈퇴에 대한 권한 없습니다.")); + .orElseThrow(() -> new InvalidTokenException("유효한 토큰이 아닙니다.")); + final User user = userRepository.findById(privateClaims.userId()) + .orElseThrow(() -> new InvalidWithdrawalException("탈퇴에 대한 권한이 없습니다.")); + final OAuth2UserInformationProvider provider = + providerComposite.findProvider(user.getOauthInformation().getOauth2Type()); + + validateCanWithdrawal(user); user.withdrawal(); blackListTokenService.registerBlackListToken(accessToken, refreshToken); deviceTokenRepository.deleteByUserId(user.getId()); - provider.unlinkUserBy(user.getOauthId()); + provider.unlinkUserBy(user.getOauthInformation().getOauthId()); + } + + private void validateCanWithdrawal(final User user) { + final LocalDateTime now = LocalDateTime.now(); + + if (auctionRepository.existsBySellerIdAndAuctionStatusIsOngoing(user.getId(), now)) { + throw new WithdrawalNotAllowedException("등록한 경매 중 현재 진행 중인 것이 있기에 탈퇴할 수 없습니다."); + } + if (auctionRepository.existsLastBidByUserIdAndAuctionStatusIsOngoing(user.getId(), now)) { + throw new WithdrawalNotAllowedException("마지막 입찰자로 등록되어 있는 것이 있기에 탈퇴할 수 없습니다."); + } } } diff --git a/backend/ddang/src/main/java/com/ddang/ddang/authentication/application/AuthenticationUserService.java b/backend/ddang/src/main/java/com/ddang/ddang/authentication/application/AuthenticationUserService.java index 69486e4bd..0ffa6dd82 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/authentication/application/AuthenticationUserService.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/authentication/application/AuthenticationUserService.java @@ -1,6 +1,6 @@ package com.ddang.ddang.authentication.application; -import com.ddang.ddang.user.infrastructure.persistence.JpaUserRepository; +import com.ddang.ddang.user.domain.repository.UserRepository; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -10,7 +10,7 @@ @Transactional(readOnly = true) public class AuthenticationUserService { - private final JpaUserRepository userRepository; + private final UserRepository userRepository; public boolean isWithdrawal(final Long userId) { return userRepository.existsByIdAndDeletedIsTrue(userId); diff --git a/backend/ddang/src/main/java/com/ddang/ddang/authentication/application/BlackListTokenService.java b/backend/ddang/src/main/java/com/ddang/ddang/authentication/application/BlackListTokenService.java index ac271a1a5..30c6c0386 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/authentication/application/BlackListTokenService.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/authentication/application/BlackListTokenService.java @@ -4,7 +4,7 @@ import com.ddang.ddang.authentication.domain.TokenDecoder; import com.ddang.ddang.authentication.domain.TokenType; import com.ddang.ddang.authentication.domain.exception.EmptyTokenException; -import com.ddang.ddang.authentication.infrastructure.persistence.JpaBlackListTokenRepository; +import com.ddang.ddang.authentication.domain.repository.BlackListTokenRepository; import java.util.ArrayList; import java.util.List; import lombok.RequiredArgsConstructor; @@ -16,7 +16,7 @@ @Transactional(readOnly = true) public class BlackListTokenService { - private final JpaBlackListTokenRepository blackListTokenRepository; + private final BlackListTokenRepository blackListTokenRepository; private final TokenDecoder tokenDecoder; @Transactional diff --git a/backend/ddang/src/main/java/com/ddang/ddang/authentication/application/dto/LoginInformationDto.java b/backend/ddang/src/main/java/com/ddang/ddang/authentication/application/dto/LoginInformationDto.java new file mode 100644 index 000000000..0972837f3 --- /dev/null +++ b/backend/ddang/src/main/java/com/ddang/ddang/authentication/application/dto/LoginInformationDto.java @@ -0,0 +1,11 @@ +package com.ddang.ddang.authentication.application.dto; + +public record LoginInformationDto(TokenDto tokenDto, boolean isSignUpUser) { + + public static LoginInformationDto of( + final TokenDto tokenDto, + final LoginUserInformationDto loginUserInformationDto + ) { + return new LoginInformationDto(tokenDto, loginUserInformationDto.persisted()); + } +} diff --git a/backend/ddang/src/main/java/com/ddang/ddang/authentication/application/dto/LoginUserInformationDto.java b/backend/ddang/src/main/java/com/ddang/ddang/authentication/application/dto/LoginUserInformationDto.java new file mode 100644 index 000000000..d3cfef0c5 --- /dev/null +++ b/backend/ddang/src/main/java/com/ddang/ddang/authentication/application/dto/LoginUserInformationDto.java @@ -0,0 +1,6 @@ +package com.ddang.ddang.authentication.application.dto; + +import com.ddang.ddang.user.domain.User; + +public record LoginUserInformationDto(User user, boolean persisted) { +} diff --git a/backend/ddang/src/main/java/com/ddang/ddang/authentication/application/exception/WithdrawalNotAllowedException.java b/backend/ddang/src/main/java/com/ddang/ddang/authentication/application/exception/WithdrawalNotAllowedException.java new file mode 100644 index 000000000..e534213de --- /dev/null +++ b/backend/ddang/src/main/java/com/ddang/ddang/authentication/application/exception/WithdrawalNotAllowedException.java @@ -0,0 +1,8 @@ +package com.ddang.ddang.authentication.application.exception; + +public class WithdrawalNotAllowedException extends IllegalArgumentException { + + public WithdrawalNotAllowedException(final String message) { + super(message); + } +} diff --git a/backend/ddang/src/main/java/com/ddang/ddang/authentication/domain/repository/BlackListTokenRepository.java b/backend/ddang/src/main/java/com/ddang/ddang/authentication/domain/repository/BlackListTokenRepository.java new file mode 100644 index 000000000..1c7039fff --- /dev/null +++ b/backend/ddang/src/main/java/com/ddang/ddang/authentication/domain/repository/BlackListTokenRepository.java @@ -0,0 +1,14 @@ +package com.ddang.ddang.authentication.domain.repository; + +import com.ddang.ddang.authentication.domain.BlackListToken; +import com.ddang.ddang.authentication.domain.TokenType; +import java.util.List; + +public interface BlackListTokenRepository { + + BlackListToken save(final BlackListToken blackListToken); + + List saveAll(final List blackListTokens); + + boolean existsByTokenTypeAndToken(final TokenType tokenType, final String accessToken); +} diff --git a/backend/ddang/src/main/java/com/ddang/ddang/authentication/infrastructure/persistence/BlackListTokenRepositoryImpl.java b/backend/ddang/src/main/java/com/ddang/ddang/authentication/infrastructure/persistence/BlackListTokenRepositoryImpl.java new file mode 100644 index 000000000..8e8d5c88a --- /dev/null +++ b/backend/ddang/src/main/java/com/ddang/ddang/authentication/infrastructure/persistence/BlackListTokenRepositoryImpl.java @@ -0,0 +1,30 @@ +package com.ddang.ddang.authentication.infrastructure.persistence; + +import com.ddang.ddang.authentication.domain.BlackListToken; +import com.ddang.ddang.authentication.domain.TokenType; +import com.ddang.ddang.authentication.domain.repository.BlackListTokenRepository; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Repository; + +@Repository +@RequiredArgsConstructor +public class BlackListTokenRepositoryImpl implements BlackListTokenRepository { + + private final JpaBlackListTokenRepository jpaBlackListTokenRepository; + + @Override + public BlackListToken save(final BlackListToken blackListToken) { + return jpaBlackListTokenRepository.save(blackListToken); + } + + @Override + public List saveAll(final List blackListTokens) { + return jpaBlackListTokenRepository.saveAll(blackListTokens); + } + + @Override + public boolean existsByTokenTypeAndToken(final TokenType tokenType, final String accessToken) { + return jpaBlackListTokenRepository.existsByTokenTypeAndToken(tokenType, accessToken); + } +} diff --git a/backend/ddang/src/main/java/com/ddang/ddang/authentication/presentation/AuthenticationController.java b/backend/ddang/src/main/java/com/ddang/ddang/authentication/presentation/AuthenticationController.java index 0e2b0eed7..52f02a9f0 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/authentication/presentation/AuthenticationController.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/authentication/presentation/AuthenticationController.java @@ -2,12 +2,14 @@ import com.ddang.ddang.authentication.application.AuthenticationService; import com.ddang.ddang.authentication.application.BlackListTokenService; +import com.ddang.ddang.authentication.application.dto.LoginInformationDto; import com.ddang.ddang.authentication.application.dto.TokenDto; import com.ddang.ddang.authentication.infrastructure.oauth2.Oauth2Type; import com.ddang.ddang.authentication.presentation.dto.request.LoginTokenRequest; import com.ddang.ddang.authentication.presentation.dto.request.LogoutRequest; import com.ddang.ddang.authentication.presentation.dto.request.RefreshTokenRequest; import com.ddang.ddang.authentication.presentation.dto.request.WithdrawalRequest; +import com.ddang.ddang.authentication.presentation.dto.response.LoginInformationResponse; import com.ddang.ddang.authentication.presentation.dto.response.TokenResponse; import com.ddang.ddang.authentication.presentation.dto.response.ValidatedTokenResponse; import jakarta.validation.Valid; @@ -31,17 +33,18 @@ public class AuthenticationController { private final BlackListTokenService blackListTokenService; @PostMapping("/login/{oauth2Type}") - public ResponseEntity login( + public ResponseEntity login( @PathVariable final Oauth2Type oauth2Type, @RequestBody final LoginTokenRequest request ) { - final TokenDto tokenDto = authenticationService.login(oauth2Type, request.accessToken(), request.deviceToken()); + final LoginInformationDto loginInformationDto = + authenticationService.login(oauth2Type, request.accessToken(), request.deviceToken()); - return ResponseEntity.ok(TokenResponse.from(tokenDto)); + return ResponseEntity.ok(LoginInformationResponse.from(loginInformationDto)); } @PostMapping("/refresh-token") - public ResponseEntity refreshToken(@RequestBody final RefreshTokenRequest request) { + public ResponseEntity refreshToken(@RequestBody final RefreshTokenRequest request) { final TokenDto tokenDto = authenticationService.refreshToken(request.refreshToken()); return ResponseEntity.ok(TokenResponse.from(tokenDto)); @@ -67,13 +70,12 @@ public ResponseEntity logout( .build(); } - @PostMapping("/withdrawal/{oauth2Type}") + @PostMapping("/withdrawal") public ResponseEntity withdrawal( - @PathVariable final Oauth2Type oauth2Type, @RequestHeader(HttpHeaders.AUTHORIZATION) final String accessToken, @RequestBody @Valid final WithdrawalRequest request ) { - authenticationService.withdrawal(oauth2Type, accessToken, request.refreshToken()); + authenticationService.withdrawal(accessToken, request.refreshToken()); return ResponseEntity.noContent() .build(); diff --git a/backend/ddang/src/main/java/com/ddang/ddang/authentication/presentation/dto/response/LoginInformationResponse.java b/backend/ddang/src/main/java/com/ddang/ddang/authentication/presentation/dto/response/LoginInformationResponse.java new file mode 100644 index 000000000..14ff3a2a3 --- /dev/null +++ b/backend/ddang/src/main/java/com/ddang/ddang/authentication/presentation/dto/response/LoginInformationResponse.java @@ -0,0 +1,14 @@ +package com.ddang.ddang.authentication.presentation.dto.response; + +import com.ddang.ddang.authentication.application.dto.LoginInformationDto; + +public record LoginInformationResponse(String accessToken, String refreshToken, boolean isSignUpUser) { + + public static LoginInformationResponse from(final LoginInformationDto dto) { + return new LoginInformationResponse( + dto.tokenDto().accessToken(), + dto.tokenDto().refreshToken(), + dto.isSignUpUser() + ); + } +} diff --git a/backend/ddang/src/main/java/com/ddang/ddang/bid/application/BidService.java b/backend/ddang/src/main/java/com/ddang/ddang/bid/application/BidService.java index 49e6375b5..d2c6a4e97 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/bid/application/BidService.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/bid/application/BidService.java @@ -2,8 +2,9 @@ import com.ddang.ddang.auction.application.exception.AuctionNotFoundException; import com.ddang.ddang.auction.domain.Auction; -import com.ddang.ddang.auction.infrastructure.persistence.JpaAuctionRepository; -import com.ddang.ddang.auction.infrastructure.persistence.dto.AuctionAndImageDto; +import com.ddang.ddang.auction.domain.dto.AuctionAndImageDto; +import com.ddang.ddang.auction.domain.repository.AuctionAndImageRepository; +import com.ddang.ddang.auction.domain.repository.AuctionRepository; import com.ddang.ddang.bid.application.dto.BidDto; import com.ddang.ddang.bid.application.dto.CreateBidDto; import com.ddang.ddang.bid.application.dto.ReadBidDto; @@ -13,12 +14,11 @@ import com.ddang.ddang.bid.application.exception.InvalidBidderException; import com.ddang.ddang.bid.domain.Bid; import com.ddang.ddang.bid.domain.BidPrice; -import com.ddang.ddang.bid.infrastructure.persistence.JpaBidRepository; +import com.ddang.ddang.bid.domain.repository.BidRepository; import com.ddang.ddang.user.application.exception.UserNotFoundException; import com.ddang.ddang.user.domain.User; -import com.ddang.ddang.user.infrastructure.persistence.JpaUserRepository; +import com.ddang.ddang.user.domain.repository.UserRepository; import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; import org.springframework.context.ApplicationEventPublisher; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -30,21 +30,21 @@ @Service @Transactional(readOnly = true) @RequiredArgsConstructor -@Slf4j public class BidService { private final ApplicationEventPublisher bidEventPublisher; - private final JpaAuctionRepository auctionRepository; - private final JpaUserRepository userRepository; - private final JpaBidRepository bidRepository; + private final AuctionRepository auctionRepository; + private final AuctionAndImageRepository auctionAndImageRepository; + private final UserRepository userRepository; + private final BidRepository bidRepository; @Transactional public Long create(final CreateBidDto bidDto, final String auctionImageAbsoluteUrl) { final User bidder = userRepository.findById(bidDto.userId()) .orElseThrow(() -> new UserNotFoundException("해당 사용자를 찾을 수 없습니다.")); final AuctionAndImageDto auctionAndImageDto = - auctionRepository.findDtoByAuctionId(bidDto.auctionId()) - .orElseThrow(() -> new AuctionNotFoundException("해당 경매를 찾을 수 없습니다.")); + auctionAndImageRepository.findDtoByAuctionId(bidDto.auctionId()) + .orElseThrow(() -> new AuctionNotFoundException("해당 경매를 찾을 수 없습니다.")); final Auction auction = auctionAndImageDto.auction(); checkInvalidAuction(auction); @@ -87,18 +87,18 @@ private void checkInvalidAuction(final Auction auction) { } private void checkInvalidBid(final Auction auction, final User bidder, final CreateBidDto bidDto) { - final Bid lastBid = bidRepository.findLastBidByAuctionId(bidDto.auctionId()); + final Optional lastBid = bidRepository.findLastBidByAuctionId(bidDto.auctionId()); final BidPrice bidPrice = processBidPrice(bidDto.bidPrice()); checkIsSeller(auction, bidder); - if (lastBid == null) { - checkInvalidFirstBidPrice(auction, bidPrice); + if (lastBid.isPresent()) { + checkIsNotLastBidder(lastBid.get(), bidder); + checkInvalidBidPrice(lastBid.get(), bidPrice); return; } - checkIsNotLastBidder(lastBid, bidder); - checkInvalidBidPrice(lastBid, bidPrice); + checkInvalidFirstBidPrice(auction, bidPrice); } private BidPrice processBidPrice(final int value) { @@ -144,7 +144,7 @@ private Bid saveAndUpdateLastBid(final CreateBidDto bidDto, final Auction auctio public List readAllByAuctionId(final Long auctionId) { if (auctionRepository.existsById(auctionId)) { - final List bids = bidRepository.findByAuctionIdOrderByIdAsc(auctionId); + final List bids = bidRepository.findAllByAuctionId(auctionId); return bids.stream() .map(ReadBidDto::from) diff --git a/backend/ddang/src/main/java/com/ddang/ddang/bid/application/dto/BidDto.java b/backend/ddang/src/main/java/com/ddang/ddang/bid/application/dto/BidDto.java index 8787a5cf4..6be141e5a 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/bid/application/dto/BidDto.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/bid/application/dto/BidDto.java @@ -1,6 +1,6 @@ package com.ddang.ddang.bid.application.dto; -import com.ddang.ddang.auction.infrastructure.persistence.dto.AuctionAndImageDto; +import com.ddang.ddang.auction.domain.dto.AuctionAndImageDto; public record BidDto( Long previousBidderId, diff --git a/backend/ddang/src/main/java/com/ddang/ddang/bid/domain/Bid.java b/backend/ddang/src/main/java/com/ddang/ddang/bid/domain/Bid.java index db4d6d0fe..be5bcf03d 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/bid/domain/Bid.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/bid/domain/Bid.java @@ -23,8 +23,8 @@ @Entity @NoArgsConstructor(access = AccessLevel.PROTECTED) @Getter -@EqualsAndHashCode(of = "id") -@ToString(of = {"id", "bidPrice"}) +@EqualsAndHashCode(of = "id", callSuper = false) +@ToString(of = {"id", "price"}) public class Bid extends BaseCreateTimeEntity { @Id diff --git a/backend/ddang/src/main/java/com/ddang/ddang/bid/domain/repository/BidRepository.java b/backend/ddang/src/main/java/com/ddang/ddang/bid/domain/repository/BidRepository.java new file mode 100644 index 000000000..2005c8b69 --- /dev/null +++ b/backend/ddang/src/main/java/com/ddang/ddang/bid/domain/repository/BidRepository.java @@ -0,0 +1,15 @@ +package com.ddang.ddang.bid.domain.repository; + +import com.ddang.ddang.bid.domain.Bid; + +import java.util.List; +import java.util.Optional; + +public interface BidRepository { + + Bid save(final Bid bid); + + List findAllByAuctionId(final Long auctionId); + + Optional findLastBidByAuctionId(final Long auctionId); +} diff --git a/backend/ddang/src/main/java/com/ddang/ddang/bid/infrastructure/persistence/BidRepositoryImpl.java b/backend/ddang/src/main/java/com/ddang/ddang/bid/infrastructure/persistence/BidRepositoryImpl.java new file mode 100644 index 000000000..4b1ca7073 --- /dev/null +++ b/backend/ddang/src/main/java/com/ddang/ddang/bid/infrastructure/persistence/BidRepositoryImpl.java @@ -0,0 +1,31 @@ +package com.ddang.ddang.bid.infrastructure.persistence; + +import com.ddang.ddang.bid.domain.Bid; +import com.ddang.ddang.bid.domain.repository.BidRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.Optional; + +@Repository +@RequiredArgsConstructor +public class BidRepositoryImpl implements BidRepository { + + private final JpaBidRepository jpaBidRepository; + + @Override + public Bid save(final Bid bid) { + return jpaBidRepository.save(bid); + } + + @Override + public List findAllByAuctionId(final Long auctionId) { + return jpaBidRepository.findAllByAuctionIdOrderByIdAsc(auctionId); + } + + @Override + public Optional findLastBidByAuctionId(final Long auctionId) { + return jpaBidRepository.findLastBidByAuctionId(auctionId); + } +} diff --git a/backend/ddang/src/main/java/com/ddang/ddang/bid/infrastructure/persistence/JpaBidRepository.java b/backend/ddang/src/main/java/com/ddang/ddang/bid/infrastructure/persistence/JpaBidRepository.java index 7d12de1de..9c3a2e690 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/bid/infrastructure/persistence/JpaBidRepository.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/bid/infrastructure/persistence/JpaBidRepository.java @@ -6,11 +6,12 @@ import org.springframework.data.repository.query.Param; import java.util.List; +import java.util.Optional; public interface JpaBidRepository extends JpaRepository { - List findByAuctionIdOrderByIdAsc(final Long id); + List findAllByAuctionIdOrderByIdAsc(final Long auctionId); - @Query("select b from Bid b where b.auction.id = :auctionId order by b.id desc limit 1") - Bid findLastBidByAuctionId(@Param("auctionId") final Long id); + @Query("SELECT b FROM Bid b WHERE b.auction.id = :auctionId ORDER BY b.id DESC LIMIT 1") + Optional findLastBidByAuctionId(@Param("auctionId") final Long auctionId); } diff --git a/backend/ddang/src/main/java/com/ddang/ddang/bid/presentation/BidController.java b/backend/ddang/src/main/java/com/ddang/ddang/bid/presentation/BidController.java index 89cba81ba..1da1c3378 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/bid/presentation/BidController.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/bid/presentation/BidController.java @@ -7,7 +7,6 @@ import com.ddang.ddang.bid.application.dto.ReadBidDto; import com.ddang.ddang.bid.presentation.dto.request.CreateBidRequest; import com.ddang.ddang.bid.presentation.dto.response.ReadBidResponse; -import com.ddang.ddang.bid.presentation.dto.response.ReadBidsResponse; import com.ddang.ddang.image.presentation.util.ImageRelativeUrl; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; @@ -41,13 +40,11 @@ public ResponseEntity create( } @GetMapping("/{auctionId}") - public ResponseEntity readAllByAuctionId(@PathVariable final Long auctionId) { + public ResponseEntity> readAllByAuctionId(@PathVariable final Long auctionId) { final List readBidDtos = bidService.readAllByAuctionId(auctionId); - final List readBidResponses = readBidDtos.stream() - .map(ReadBidResponse::from) - .toList(); - - final ReadBidsResponse response = new ReadBidsResponse(readBidResponses); + final List response = readBidDtos.stream() + .map(ReadBidResponse::from) + .toList(); return ResponseEntity.ok(response); } diff --git a/backend/ddang/src/main/java/com/ddang/ddang/bid/presentation/dto/response/ReadBidsResponse.java b/backend/ddang/src/main/java/com/ddang/ddang/bid/presentation/dto/response/ReadBidsResponse.java deleted file mode 100644 index 31bca3148..000000000 --- a/backend/ddang/src/main/java/com/ddang/ddang/bid/presentation/dto/response/ReadBidsResponse.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.ddang.ddang.bid.presentation.dto.response; - -import java.util.List; - -public record ReadBidsResponse(List bids) { -} diff --git a/backend/ddang/src/main/java/com/ddang/ddang/category/application/CategoryService.java b/backend/ddang/src/main/java/com/ddang/ddang/category/application/CategoryService.java index 48df85d4a..e47ea0a66 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/category/application/CategoryService.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/category/application/CategoryService.java @@ -3,19 +3,18 @@ import com.ddang.ddang.category.application.dto.ReadCategoryDto; import com.ddang.ddang.category.application.exception.CategoryNotFoundException; import com.ddang.ddang.category.domain.Category; -import com.ddang.ddang.category.infrastructure.persistence.JpaCategoryRepository; +import com.ddang.ddang.category.domain.repository.CategoryRepository; +import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.util.List; - @Service @Transactional(readOnly = true) @RequiredArgsConstructor public class CategoryService { - private final JpaCategoryRepository categoryRepository; + private final CategoryRepository categoryRepository; public List readAllMain() { final List mainCategories = categoryRepository.findMainAllByMainCategoryIsNull(); diff --git a/backend/ddang/src/main/java/com/ddang/ddang/category/domain/repository/CategoryRepository.java b/backend/ddang/src/main/java/com/ddang/ddang/category/domain/repository/CategoryRepository.java new file mode 100644 index 000000000..5b10afdd4 --- /dev/null +++ b/backend/ddang/src/main/java/com/ddang/ddang/category/domain/repository/CategoryRepository.java @@ -0,0 +1,14 @@ +package com.ddang.ddang.category.domain.repository; + +import com.ddang.ddang.category.domain.Category; +import java.util.List; +import java.util.Optional; + +public interface CategoryRepository { + + List findMainAllByMainCategoryIsNull(); + + List findSubAllByMainCategoryId(final Long mainCategoryId); + + Optional findSubCategoryById(final Long subCategoryId); +} diff --git a/backend/ddang/src/main/java/com/ddang/ddang/category/infrastructure/persistence/CategoryRepositoryImpl.java b/backend/ddang/src/main/java/com/ddang/ddang/category/infrastructure/persistence/CategoryRepositoryImpl.java new file mode 100644 index 000000000..58e3faed0 --- /dev/null +++ b/backend/ddang/src/main/java/com/ddang/ddang/category/infrastructure/persistence/CategoryRepositoryImpl.java @@ -0,0 +1,30 @@ +package com.ddang.ddang.category.infrastructure.persistence; + +import com.ddang.ddang.category.domain.Category; +import com.ddang.ddang.category.domain.repository.CategoryRepository; +import java.util.List; +import java.util.Optional; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Repository; + +@Repository +@RequiredArgsConstructor +public class CategoryRepositoryImpl implements CategoryRepository { + + private final JpaCategoryRepository jpaCategoryRepository; + + @Override + public List findMainAllByMainCategoryIsNull() { + return jpaCategoryRepository.findMainAllByMainCategoryIsNull(); + } + + @Override + public List findSubAllByMainCategoryId(final Long mainCategoryId) { + return jpaCategoryRepository.findSubAllByMainCategoryId(mainCategoryId); + } + + @Override + public Optional findSubCategoryById(final Long subCategoryId) { + return jpaCategoryRepository.findSubCategoryById(subCategoryId); + } +} diff --git a/backend/ddang/src/main/java/com/ddang/ddang/chat/application/ChatRoomService.java b/backend/ddang/src/main/java/com/ddang/ddang/chat/application/ChatRoomService.java index d723e9f84..efb3d78d6 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/chat/application/ChatRoomService.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/chat/application/ChatRoomService.java @@ -4,7 +4,7 @@ import com.ddang.ddang.auction.application.exception.AuctionNotFoundException; import com.ddang.ddang.auction.domain.Auction; import com.ddang.ddang.auction.domain.exception.WinnerNotFoundException; -import com.ddang.ddang.auction.infrastructure.persistence.JpaAuctionRepository; +import com.ddang.ddang.auction.domain.repository.AuctionRepository; import com.ddang.ddang.authentication.domain.dto.AuthenticationUserInfo; import com.ddang.ddang.chat.application.dto.CreateChatRoomDto; import com.ddang.ddang.chat.application.dto.ReadChatRoomWithLastMessageDto; @@ -13,14 +13,14 @@ import com.ddang.ddang.chat.application.exception.InvalidAuctionToChatException; import com.ddang.ddang.chat.application.exception.InvalidUserToChat; import com.ddang.ddang.chat.domain.ChatRoom; -import com.ddang.ddang.chat.infrastructure.persistence.JpaChatRoomRepository; -import com.ddang.ddang.chat.infrastructure.persistence.QuerydslChatRoomAndImageRepositoryImpl; -import com.ddang.ddang.chat.infrastructure.persistence.QuerydslChatRoomAndMessageAndImageRepository; -import com.ddang.ddang.chat.infrastructure.persistence.dto.ChatRoomAndImageDto; -import com.ddang.ddang.chat.infrastructure.persistence.dto.ChatRoomAndMessageAndImageDto; +import com.ddang.ddang.chat.domain.dto.ChatRoomAndImageDto; +import com.ddang.ddang.chat.domain.dto.ChatRoomAndMessageAndImageDto; +import com.ddang.ddang.chat.domain.repository.ChatRoomAndImageRepository; +import com.ddang.ddang.chat.domain.repository.ChatRoomAndMessageAndImageRepository; +import com.ddang.ddang.chat.domain.repository.ChatRoomRepository; import com.ddang.ddang.user.application.exception.UserNotFoundException; import com.ddang.ddang.user.domain.User; -import com.ddang.ddang.user.infrastructure.persistence.JpaUserRepository; +import com.ddang.ddang.user.domain.repository.UserRepository; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -35,17 +35,17 @@ public class ChatRoomService { private static final Long DEFAULT_CHAT_ROOM_ID = null; - private final JpaChatRoomRepository chatRoomRepository; - private final QuerydslChatRoomAndImageRepositoryImpl querydslChatRoomAndImageRepository; - private final QuerydslChatRoomAndMessageAndImageRepository querydslChatRoomAndMessageAndImageRepository; - private final JpaUserRepository userRepository; - private final JpaAuctionRepository auctionRepository; + private final ChatRoomRepository chatRoomRepository; + private final ChatRoomAndImageRepository chatRoomAndImageRepository; + private final ChatRoomAndMessageAndImageRepository chatRoomAndMessageAndImageRepository; + private final UserRepository userRepository; + private final AuctionRepository auctionRepository; @Transactional public Long create(final Long userId, final CreateChatRoomDto chatRoomDto) { final User findUser = userRepository.findById(userId) .orElseThrow(() -> new UserNotFoundException("사용자 정보를 찾을 수 없습니다.")); - final Auction findAuction = auctionRepository.findAuctionById(chatRoomDto.auctionId()) + final Auction findAuction = auctionRepository.findTotalAuctionById(chatRoomDto.auctionId()) .orElseThrow(() -> new AuctionNotFoundException("해당 경매를 찾을 수 없습니다.") ); @@ -87,7 +87,7 @@ public List readAllByUserId(final Long userId) { final User findUser = userRepository.findById(userId) .orElseThrow(() -> new UserNotFoundException("사용자 정보를 찾을 수 없습니다.")); final List chatRoomAndMessageAndImageQueryProjectionDtos = - querydslChatRoomAndMessageAndImageRepository.findAllChatRoomInfoByUserIdOrderByLastMessage(findUser.getId()); + chatRoomAndMessageAndImageRepository.findAllChatRoomInfoByUserIdOrderByLastMessage(findUser.getId()); return chatRoomAndMessageAndImageQueryProjectionDtos.stream() .map(dto -> ReadChatRoomWithLastMessageDto.of(findUser, dto)) @@ -98,10 +98,10 @@ public ReadParticipatingChatRoomDto readByChatRoomId(final Long chatRoomId, fina final User findUser = userRepository.findById(userId) .orElseThrow(() -> new UserNotFoundException("사용자 정보를 찾을 수 없습니다.")); final ChatRoomAndImageDto chatRoomAndImageDto = - querydslChatRoomAndImageRepository.findChatRoomById(chatRoomId) - .orElseThrow(() -> new ChatRoomNotFoundException( - "지정한 아이디에 대한 채팅방을 찾을 수 없습니다." - )); + chatRoomAndImageRepository.findChatRoomById(chatRoomId) + .orElseThrow(() -> new ChatRoomNotFoundException( + "지정한 아이디에 대한 채팅방을 찾을 수 없습니다." + )); checkAccessible(findUser, chatRoomAndImageDto.chatRoom()); return ReadParticipatingChatRoomDto.of(findUser, chatRoomAndImageDto); @@ -115,11 +115,16 @@ private void checkAccessible(final User findUser, final ChatRoom chatRoom) { public ReadChatRoomDto readChatInfoByAuctionId(final Long auctionId, final AuthenticationUserInfo userInfo) { final User findUser = userRepository.findById(userInfo.userId()) - .orElseThrow(() -> new UserNotFoundException("회원 정보를 찾을 수 없습니다.")); - final Auction findAuction = auctionRepository.findAuctionById(auctionId) + .orElse(User.EMPTY_USER); + final Auction findAuction = auctionRepository.findTotalAuctionById(auctionId) .orElseThrow(() -> new AuctionNotFoundException( "지정한 아이디에 대한 경매를 찾을 수 없습니다." )); + + if (findUser == User.EMPTY_USER) { + return ReadChatRoomDto.CANNOT_CHAT_DTO; + } + final Long chatRoomId = chatRoomRepository.findChatRoomIdByAuctionId(findAuction.getId()) .orElse(DEFAULT_CHAT_ROOM_ID); diff --git a/backend/ddang/src/main/java/com/ddang/ddang/chat/application/MessageService.java b/backend/ddang/src/main/java/com/ddang/ddang/chat/application/MessageService.java index 7b26fc486..35493d0f5 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/chat/application/MessageService.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/chat/application/MessageService.java @@ -1,7 +1,6 @@ package com.ddang.ddang.chat.application; import com.ddang.ddang.chat.application.dto.CreateMessageDto; -import com.ddang.ddang.chat.application.dto.MessageDto; import com.ddang.ddang.chat.application.dto.ReadMessageDto; import com.ddang.ddang.chat.application.event.MessageNotificationEvent; import com.ddang.ddang.chat.application.exception.ChatRoomNotFoundException; @@ -9,12 +8,12 @@ import com.ddang.ddang.chat.application.exception.UnableToChatException; import com.ddang.ddang.chat.domain.ChatRoom; import com.ddang.ddang.chat.domain.Message; -import com.ddang.ddang.chat.infrastructure.persistence.JpaChatRoomRepository; -import com.ddang.ddang.chat.infrastructure.persistence.JpaMessageRepository; +import com.ddang.ddang.chat.domain.repository.ChatRoomRepository; +import com.ddang.ddang.chat.domain.repository.MessageRepository; import com.ddang.ddang.chat.presentation.dto.request.ReadMessageRequest; import com.ddang.ddang.user.application.exception.UserNotFoundException; import com.ddang.ddang.user.domain.User; -import com.ddang.ddang.user.infrastructure.persistence.JpaUserRepository; +import com.ddang.ddang.user.domain.repository.UserRepository; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.context.ApplicationEventPublisher; @@ -30,9 +29,9 @@ public class MessageService { private final ApplicationEventPublisher messageEventPublisher; - private final JpaMessageRepository messageRepository; - private final JpaChatRoomRepository chatRoomRepository; - private final JpaUserRepository userRepository; + private final MessageRepository messageRepository; + private final ChatRoomRepository chatRoomRepository; + private final UserRepository userRepository; @Transactional public Long create(final CreateMessageDto dto, final String profileImageAbsoluteUrl) { @@ -54,8 +53,7 @@ public Long create(final CreateMessageDto dto, final String profileImageAbsolute final Message persistMessage = messageRepository.save(message); - final MessageDto messageDto = MessageDto.of(persistMessage, chatRoom, writer, receiver, profileImageAbsoluteUrl); - messageEventPublisher.publishEvent(new MessageNotificationEvent(messageDto)); + messageEventPublisher.publishEvent(new MessageNotificationEvent(persistMessage, profileImageAbsoluteUrl)); return persistMessage.getId(); } @@ -73,7 +71,7 @@ public List readAllByLastMessageId(final ReadMessageRequest requ validateLastMessageId(request.lastMessageId()); } - final List readMessages = messageRepository.findMessagesAllByLastMessageId( + final List readMessages = messageRepository.findAllByLastMessageId( request.messageReaderId(), chatRoom.getId(), request.lastMessageId() diff --git a/backend/ddang/src/main/java/com/ddang/ddang/chat/application/dto/ReadChatRoomWithLastMessageDto.java b/backend/ddang/src/main/java/com/ddang/ddang/chat/application/dto/ReadChatRoomWithLastMessageDto.java index 012b60754..36e3d0382 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/chat/application/dto/ReadChatRoomWithLastMessageDto.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/chat/application/dto/ReadChatRoomWithLastMessageDto.java @@ -2,7 +2,7 @@ import com.ddang.ddang.chat.domain.ChatRoom; import com.ddang.ddang.chat.domain.Message; -import com.ddang.ddang.chat.infrastructure.persistence.dto.ChatRoomAndMessageAndImageDto; +import com.ddang.ddang.chat.domain.dto.ChatRoomAndMessageAndImageDto; import com.ddang.ddang.image.domain.AuctionImage; import com.ddang.ddang.user.domain.User; diff --git a/backend/ddang/src/main/java/com/ddang/ddang/chat/application/dto/ReadParticipatingChatRoomDto.java b/backend/ddang/src/main/java/com/ddang/ddang/chat/application/dto/ReadParticipatingChatRoomDto.java index 318040fc1..7ed2e64b9 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/chat/application/dto/ReadParticipatingChatRoomDto.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/chat/application/dto/ReadParticipatingChatRoomDto.java @@ -1,7 +1,7 @@ package com.ddang.ddang.chat.application.dto; import com.ddang.ddang.chat.domain.ChatRoom; -import com.ddang.ddang.chat.infrastructure.persistence.dto.ChatRoomAndImageDto; +import com.ddang.ddang.chat.domain.dto.ChatRoomAndImageDto; import com.ddang.ddang.user.domain.User; public record ReadParticipatingChatRoomDto( diff --git a/backend/ddang/src/main/java/com/ddang/ddang/chat/application/event/MessageNotificationEvent.java b/backend/ddang/src/main/java/com/ddang/ddang/chat/application/event/MessageNotificationEvent.java index 56bf90d60..9597cafc9 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/chat/application/event/MessageNotificationEvent.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/chat/application/event/MessageNotificationEvent.java @@ -1,6 +1,6 @@ package com.ddang.ddang.chat.application.event; -import com.ddang.ddang.chat.application.dto.MessageDto; +import com.ddang.ddang.chat.domain.Message; -public record MessageNotificationEvent(MessageDto messageDto) { +public record MessageNotificationEvent(Message message, String profileImageAbsoluteUrl) { } diff --git a/backend/ddang/src/main/java/com/ddang/ddang/chat/infrastructure/persistence/dto/ChatRoomAndImageDto.java b/backend/ddang/src/main/java/com/ddang/ddang/chat/domain/dto/ChatRoomAndImageDto.java similarity index 75% rename from backend/ddang/src/main/java/com/ddang/ddang/chat/infrastructure/persistence/dto/ChatRoomAndImageDto.java rename to backend/ddang/src/main/java/com/ddang/ddang/chat/domain/dto/ChatRoomAndImageDto.java index 1b690588c..32cfee842 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/chat/infrastructure/persistence/dto/ChatRoomAndImageDto.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/chat/domain/dto/ChatRoomAndImageDto.java @@ -1,4 +1,4 @@ -package com.ddang.ddang.chat.infrastructure.persistence.dto; +package com.ddang.ddang.chat.domain.dto; import com.ddang.ddang.chat.domain.ChatRoom; import com.ddang.ddang.image.domain.AuctionImage; diff --git a/backend/ddang/src/main/java/com/ddang/ddang/chat/infrastructure/persistence/dto/ChatRoomAndMessageAndImageDto.java b/backend/ddang/src/main/java/com/ddang/ddang/chat/domain/dto/ChatRoomAndMessageAndImageDto.java similarity index 80% rename from backend/ddang/src/main/java/com/ddang/ddang/chat/infrastructure/persistence/dto/ChatRoomAndMessageAndImageDto.java rename to backend/ddang/src/main/java/com/ddang/ddang/chat/domain/dto/ChatRoomAndMessageAndImageDto.java index bafad0a34..2e7e6ea6b 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/chat/infrastructure/persistence/dto/ChatRoomAndMessageAndImageDto.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/chat/domain/dto/ChatRoomAndMessageAndImageDto.java @@ -1,4 +1,4 @@ -package com.ddang.ddang.chat.infrastructure.persistence.dto; +package com.ddang.ddang.chat.domain.dto; import com.ddang.ddang.chat.domain.ChatRoom; import com.ddang.ddang.chat.domain.Message; diff --git a/backend/ddang/src/main/java/com/ddang/ddang/chat/domain/repository/ChatRoomAndImageRepository.java b/backend/ddang/src/main/java/com/ddang/ddang/chat/domain/repository/ChatRoomAndImageRepository.java new file mode 100644 index 000000000..a39b118f0 --- /dev/null +++ b/backend/ddang/src/main/java/com/ddang/ddang/chat/domain/repository/ChatRoomAndImageRepository.java @@ -0,0 +1,10 @@ +package com.ddang.ddang.chat.domain.repository; + +import com.ddang.ddang.chat.domain.dto.ChatRoomAndImageDto; + +import java.util.Optional; + +public interface ChatRoomAndImageRepository { + + Optional findChatRoomById(final Long chatRoomId); +} diff --git a/backend/ddang/src/main/java/com/ddang/ddang/chat/domain/repository/ChatRoomAndMessageAndImageRepository.java b/backend/ddang/src/main/java/com/ddang/ddang/chat/domain/repository/ChatRoomAndMessageAndImageRepository.java new file mode 100644 index 000000000..027f94c37 --- /dev/null +++ b/backend/ddang/src/main/java/com/ddang/ddang/chat/domain/repository/ChatRoomAndMessageAndImageRepository.java @@ -0,0 +1,10 @@ +package com.ddang.ddang.chat.domain.repository; + +import com.ddang.ddang.chat.domain.dto.ChatRoomAndMessageAndImageDto; + +import java.util.List; + +public interface ChatRoomAndMessageAndImageRepository { + + List findAllChatRoomInfoByUserIdOrderByLastMessage(final Long userId); +} diff --git a/backend/ddang/src/main/java/com/ddang/ddang/chat/domain/repository/ChatRoomRepository.java b/backend/ddang/src/main/java/com/ddang/ddang/chat/domain/repository/ChatRoomRepository.java new file mode 100644 index 000000000..3935ababc --- /dev/null +++ b/backend/ddang/src/main/java/com/ddang/ddang/chat/domain/repository/ChatRoomRepository.java @@ -0,0 +1,16 @@ +package com.ddang.ddang.chat.domain.repository; + +import com.ddang.ddang.chat.domain.ChatRoom; + +import java.util.Optional; + +public interface ChatRoomRepository { + + ChatRoom save(final ChatRoom chatRoom); + + Optional findById(final Long id); + + Optional findChatRoomIdByAuctionId(final Long auctionId); + + boolean existsByAuctionId(final Long auctionId); +} diff --git a/backend/ddang/src/main/java/com/ddang/ddang/chat/domain/repository/MessageRepository.java b/backend/ddang/src/main/java/com/ddang/ddang/chat/domain/repository/MessageRepository.java new file mode 100644 index 000000000..9bcf77d43 --- /dev/null +++ b/backend/ddang/src/main/java/com/ddang/ddang/chat/domain/repository/MessageRepository.java @@ -0,0 +1,14 @@ +package com.ddang.ddang.chat.domain.repository; + +import com.ddang.ddang.chat.domain.Message; + +import java.util.List; + +public interface MessageRepository { + + Message save(final Message message); + + boolean existsById(final Long lastMessageId); + + List findAllByLastMessageId(final Long messageReaderId, final Long chatRoomId, final Long lastMessageId); +} diff --git a/backend/ddang/src/main/java/com/ddang/ddang/chat/infrastructure/persistence/ChatRoomAndImageRepositoryImpl.java b/backend/ddang/src/main/java/com/ddang/ddang/chat/infrastructure/persistence/ChatRoomAndImageRepositoryImpl.java new file mode 100644 index 000000000..7c3c10bab --- /dev/null +++ b/backend/ddang/src/main/java/com/ddang/ddang/chat/infrastructure/persistence/ChatRoomAndImageRepositoryImpl.java @@ -0,0 +1,20 @@ +package com.ddang.ddang.chat.infrastructure.persistence; + +import com.ddang.ddang.chat.domain.dto.ChatRoomAndImageDto; +import com.ddang.ddang.chat.domain.repository.ChatRoomAndImageRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Repository; + +import java.util.Optional; + +@Repository +@RequiredArgsConstructor +public class ChatRoomAndImageRepositoryImpl implements ChatRoomAndImageRepository { + + private final QuerydslChatRoomAndImageRepository querydslChatRoomAndImageRepository; + + @Override + public Optional findChatRoomById(final Long chatRoomId) { + return querydslChatRoomAndImageRepository.findChatRoomById(chatRoomId); + } +} diff --git a/backend/ddang/src/main/java/com/ddang/ddang/chat/infrastructure/persistence/ChatRoomAndMessageAndImageRepositoryImpl.java b/backend/ddang/src/main/java/com/ddang/ddang/chat/infrastructure/persistence/ChatRoomAndMessageAndImageRepositoryImpl.java new file mode 100644 index 000000000..2047c60d5 --- /dev/null +++ b/backend/ddang/src/main/java/com/ddang/ddang/chat/infrastructure/persistence/ChatRoomAndMessageAndImageRepositoryImpl.java @@ -0,0 +1,20 @@ +package com.ddang.ddang.chat.infrastructure.persistence; + +import com.ddang.ddang.chat.domain.dto.ChatRoomAndMessageAndImageDto; +import com.ddang.ddang.chat.domain.repository.ChatRoomAndMessageAndImageRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +@RequiredArgsConstructor +public class ChatRoomAndMessageAndImageRepositoryImpl implements ChatRoomAndMessageAndImageRepository { + + private final QuerydslChatRoomAndMessageAndImageRepository querydslChatRoomAndMessageAndImageRepository; + + @Override + public List findAllChatRoomInfoByUserIdOrderByLastMessage(final Long userId) { + return querydslChatRoomAndMessageAndImageRepository.findAllChatRoomInfoByUserIdOrderByLastMessage(userId); + } +} diff --git a/backend/ddang/src/main/java/com/ddang/ddang/chat/infrastructure/persistence/ChatRoomRepositoryImpl.java b/backend/ddang/src/main/java/com/ddang/ddang/chat/infrastructure/persistence/ChatRoomRepositoryImpl.java new file mode 100644 index 000000000..a2d88ceb8 --- /dev/null +++ b/backend/ddang/src/main/java/com/ddang/ddang/chat/infrastructure/persistence/ChatRoomRepositoryImpl.java @@ -0,0 +1,35 @@ +package com.ddang.ddang.chat.infrastructure.persistence; + +import com.ddang.ddang.chat.domain.ChatRoom; +import com.ddang.ddang.chat.domain.repository.ChatRoomRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Repository; + +import java.util.Optional; + +@Repository +@RequiredArgsConstructor +public class ChatRoomRepositoryImpl implements ChatRoomRepository { + + private final JpaChatRoomRepository jpaChatRoomRepository; + + @Override + public ChatRoom save(final ChatRoom chatRoom) { + return jpaChatRoomRepository.save(chatRoom); + } + + @Override + public Optional findById(final Long id) { + return jpaChatRoomRepository.findById(id); + } + + @Override + public Optional findChatRoomIdByAuctionId(final Long auctionId) { + return jpaChatRoomRepository.findChatRoomIdByAuctionId(auctionId); + } + + @Override + public boolean existsByAuctionId(final Long auctionId) { + return jpaChatRoomRepository.existsByAuctionId(auctionId); + } +} diff --git a/backend/ddang/src/main/java/com/ddang/ddang/chat/infrastructure/persistence/JpaChatRoomRepository.java b/backend/ddang/src/main/java/com/ddang/ddang/chat/infrastructure/persistence/JpaChatRoomRepository.java index 0d4db5168..639a831ca 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/chat/infrastructure/persistence/JpaChatRoomRepository.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/chat/infrastructure/persistence/JpaChatRoomRepository.java @@ -2,8 +2,18 @@ import com.ddang.ddang.chat.domain.ChatRoom; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; -public interface JpaChatRoomRepository extends JpaRepository, QuerydslChatRoomRepository { +import java.util.Optional; + +public interface JpaChatRoomRepository extends JpaRepository { + + @Query(""" + SELECT c.id + FROM ChatRoom c + WHERE c.auction.id = :auctionId + """) + Optional findChatRoomIdByAuctionId(final Long auctionId); boolean existsByAuctionId(final Long auctionId); } diff --git a/backend/ddang/src/main/java/com/ddang/ddang/chat/infrastructure/persistence/JpaMessageRepository.java b/backend/ddang/src/main/java/com/ddang/ddang/chat/infrastructure/persistence/JpaMessageRepository.java index ebd827adf..d28ed0cf6 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/chat/infrastructure/persistence/JpaMessageRepository.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/chat/infrastructure/persistence/JpaMessageRepository.java @@ -3,5 +3,5 @@ import com.ddang.ddang.chat.domain.Message; import org.springframework.data.jpa.repository.JpaRepository; -public interface JpaMessageRepository extends JpaRepository, QuerydslMessageRepository { +public interface JpaMessageRepository extends JpaRepository { } diff --git a/backend/ddang/src/main/java/com/ddang/ddang/chat/infrastructure/persistence/MessageRepositoryImpl.java b/backend/ddang/src/main/java/com/ddang/ddang/chat/infrastructure/persistence/MessageRepositoryImpl.java new file mode 100644 index 000000000..758d8c9bd --- /dev/null +++ b/backend/ddang/src/main/java/com/ddang/ddang/chat/infrastructure/persistence/MessageRepositoryImpl.java @@ -0,0 +1,35 @@ +package com.ddang.ddang.chat.infrastructure.persistence; + +import com.ddang.ddang.chat.domain.Message; +import com.ddang.ddang.chat.domain.repository.MessageRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +@RequiredArgsConstructor +public class MessageRepositoryImpl implements MessageRepository { + + private final JpaMessageRepository jpaMessageRepository; + private final QuerydslMessageRepository querydslMessageRepository; + + @Override + public Message save(final Message message) { + return jpaMessageRepository.save(message); + } + + @Override + public boolean existsById(final Long lastMessageId) { + return jpaMessageRepository.existsById(lastMessageId); + } + + @Override + public List findAllByLastMessageId( + final Long messageReaderId, + final Long chatRoomId, + final Long lastMessageId + ) { + return querydslMessageRepository.findAllByLastMessageId(messageReaderId, chatRoomId, lastMessageId); + } +} diff --git a/backend/ddang/src/main/java/com/ddang/ddang/chat/infrastructure/persistence/QuerydslChatRoomAndImageRepository.java b/backend/ddang/src/main/java/com/ddang/ddang/chat/infrastructure/persistence/QuerydslChatRoomAndImageRepository.java index 66aa1122b..cfdd83e9e 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/chat/infrastructure/persistence/QuerydslChatRoomAndImageRepository.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/chat/infrastructure/persistence/QuerydslChatRoomAndImageRepository.java @@ -1,10 +1,48 @@ package com.ddang.ddang.chat.infrastructure.persistence; -import com.ddang.ddang.chat.infrastructure.persistence.dto.ChatRoomAndImageDto; +import com.ddang.ddang.chat.domain.dto.ChatRoomAndImageDto; +import com.ddang.ddang.chat.infrastructure.persistence.dto.ChatRoomAndImageQueryProjectionDto; +import com.ddang.ddang.chat.infrastructure.persistence.dto.QChatRoomAndImageQueryProjectionDto; +import com.querydsl.jpa.JPAExpressions; +import com.querydsl.jpa.impl.JPAQueryFactory; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Repository; import java.util.Optional; -public interface QuerydslChatRoomAndImageRepository { +import static com.ddang.ddang.auction.domain.QAuction.auction; +import static com.ddang.ddang.chat.domain.QChatRoom.chatRoom; +import static com.ddang.ddang.image.domain.QAuctionImage.auctionImage; - Optional findChatRoomById(final Long chatRoomId); +@Repository +@RequiredArgsConstructor +public class QuerydslChatRoomAndImageRepository { + + private final JPAQueryFactory queryFactory; + + public Optional findChatRoomById(final Long chatRoomId) { + + final ChatRoomAndImageQueryProjectionDto chatRoomAndImageQueryProjectionDto = + queryFactory.select(new QChatRoomAndImageQueryProjectionDto(chatRoom, auctionImage)) + .from(chatRoom) + .leftJoin(chatRoom.buyer).fetchJoin() + .leftJoin(chatRoom.auction, auction).fetchJoin() + .leftJoin(auction.seller).fetchJoin() + .leftJoin(auctionImage).on(auctionImage.id.eq( + JPAExpressions + .select(auctionImage.id.min()) + .from(auctionImage) + .where(auctionImage.auction.id.eq(auction.id)) + .groupBy(auctionImage.auction.id) + )).fetchJoin() + .leftJoin(auction.lastBid).fetchJoin() + .where(chatRoom.id.eq(chatRoomId)) + .fetchOne(); + + if (chatRoomAndImageQueryProjectionDto == null) { + return Optional.empty(); + } + + return Optional.of(chatRoomAndImageQueryProjectionDto.toDto()); + } } diff --git a/backend/ddang/src/main/java/com/ddang/ddang/chat/infrastructure/persistence/QuerydslChatRoomAndImageRepositoryImpl.java b/backend/ddang/src/main/java/com/ddang/ddang/chat/infrastructure/persistence/QuerydslChatRoomAndImageRepositoryImpl.java deleted file mode 100644 index 89ce4d1e6..000000000 --- a/backend/ddang/src/main/java/com/ddang/ddang/chat/infrastructure/persistence/QuerydslChatRoomAndImageRepositoryImpl.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.ddang.ddang.chat.infrastructure.persistence; - -import com.ddang.ddang.chat.infrastructure.persistence.dto.ChatRoomAndImageDto; -import com.ddang.ddang.chat.infrastructure.persistence.dto.ChatRoomAndImageQueryProjectionDto; -import com.ddang.ddang.chat.infrastructure.persistence.dto.QChatRoomAndImageQueryProjectionDto; -import com.querydsl.jpa.JPAExpressions; -import com.querydsl.jpa.impl.JPAQueryFactory; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Repository; - -import java.util.Optional; - -import static com.ddang.ddang.auction.domain.QAuction.auction; -import static com.ddang.ddang.chat.domain.QChatRoom.chatRoom; -import static com.ddang.ddang.image.domain.QAuctionImage.auctionImage; - -@Repository -@RequiredArgsConstructor -public class QuerydslChatRoomAndImageRepositoryImpl implements QuerydslChatRoomAndImageRepository { - - private final JPAQueryFactory queryFactory; - - @Override - public Optional findChatRoomById(final Long chatRoomId) { - - final ChatRoomAndImageQueryProjectionDto chatRoomAndImageQueryProjectionDto = - queryFactory.select(new QChatRoomAndImageQueryProjectionDto(chatRoom, auctionImage)) - .from(chatRoom) - .leftJoin(chatRoom.buyer).fetchJoin() - .leftJoin(chatRoom.auction, auction).fetchJoin() - .leftJoin(auction.seller).fetchJoin() - .leftJoin(auctionImage).on(auctionImage.id.eq( - JPAExpressions - .select(auctionImage.id.min()) - .from(auctionImage) - .where(auctionImage.auction.id.eq(auction.id)) - .groupBy(auctionImage.auction.id) - )).fetchJoin() - .leftJoin(auction.lastBid).fetchJoin() - .where(chatRoom.id.eq(chatRoomId)) - .fetchOne(); - - if (chatRoomAndImageQueryProjectionDto == null) { - return Optional.empty(); - } - - return Optional.of(chatRoomAndImageQueryProjectionDto.toDto()); - } -} diff --git a/backend/ddang/src/main/java/com/ddang/ddang/chat/infrastructure/persistence/QuerydslChatRoomAndMessageAndImageRepository.java b/backend/ddang/src/main/java/com/ddang/ddang/chat/infrastructure/persistence/QuerydslChatRoomAndMessageAndImageRepository.java index 9a5cc8092..07e7bc5f7 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/chat/infrastructure/persistence/QuerydslChatRoomAndMessageAndImageRepository.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/chat/infrastructure/persistence/QuerydslChatRoomAndMessageAndImageRepository.java @@ -1,10 +1,73 @@ package com.ddang.ddang.chat.infrastructure.persistence; -import com.ddang.ddang.chat.infrastructure.persistence.dto.ChatRoomAndMessageAndImageDto; +import com.ddang.ddang.chat.domain.dto.ChatRoomAndMessageAndImageDto; +import com.ddang.ddang.chat.infrastructure.persistence.dto.ChatRoomAndMessageAndImageQueryProjectionDto; +import com.ddang.ddang.chat.infrastructure.persistence.dto.QChatRoomAndMessageAndImageQueryProjectionDto; +import com.querydsl.core.types.dsl.BooleanExpression; +import com.querydsl.jpa.JPAExpressions; +import com.querydsl.jpa.impl.JPAQueryFactory; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Repository; import java.util.List; +import java.util.Objects; -public interface QuerydslChatRoomAndMessageAndImageRepository { +import static com.ddang.ddang.auction.domain.QAuction.auction; +import static com.ddang.ddang.chat.domain.QChatRoom.chatRoom; +import static com.ddang.ddang.chat.domain.QMessage.message; +import static com.ddang.ddang.image.domain.QAuctionImage.auctionImage; +import static java.util.Comparator.comparing; - List findAllChatRoomInfoByUserIdOrderByLastMessage(final Long userId); +@Repository +@RequiredArgsConstructor +public class QuerydslChatRoomAndMessageAndImageRepository { + + private final JPAQueryFactory queryFactory; + + public List findAllChatRoomInfoByUserIdOrderByLastMessage(final Long userId) { + final List unsortedDtos = + queryFactory.select(new QChatRoomAndMessageAndImageQueryProjectionDto(chatRoom, message, auctionImage)) + .from(chatRoom) + .leftJoin(chatRoom.buyer).fetchJoin() + .leftJoin(chatRoom.auction, auction).fetchJoin() + .leftJoin(auction.seller).fetchJoin() + .leftJoin(auctionImage).on(auctionImage.id.eq( + JPAExpressions + .select(auctionImage.id.min()) + .from(auctionImage) + .where(auctionImage.auction.id.eq(auction.id)) + .groupBy(auctionImage.auction.id) + )).fetchJoin() + .leftJoin(auction.lastBid).fetchJoin() + .leftJoin(message).on(message.id.eq( + JPAExpressions + .select(message.id.max()) + .from(message) + .where(message.chatRoom.id.eq(chatRoom.id)) + .groupBy(message.chatRoom.id) + )).fetchJoin() + .where(isSellerOrWinner(userId)) + .fetch(); + + return sortByLastMessageIdDesc(unsortedDtos); + } + + private List sortByLastMessageIdDesc( + final List unsortedDtos + ) { + return unsortedDtos.stream() + .filter((ChatRoomAndMessageAndImageQueryProjectionDto unsortedDto) -> + Objects.nonNull(unsortedDto.message()) + ).sorted(comparing( + (ChatRoomAndMessageAndImageQueryProjectionDto unsortedDto) -> + unsortedDto.message().getId() + ).reversed() + ).map(ChatRoomAndMessageAndImageQueryProjectionDto::toSortedDto) + .toList(); + } + + private BooleanExpression isSellerOrWinner(final Long userId) { + return (auction.seller.id.eq(userId)) + .or(chatRoom.buyer.id.eq(userId)); + } } diff --git a/backend/ddang/src/main/java/com/ddang/ddang/chat/infrastructure/persistence/QuerydslChatRoomAndMessageAndImageRepositoryImpl.java b/backend/ddang/src/main/java/com/ddang/ddang/chat/infrastructure/persistence/QuerydslChatRoomAndMessageAndImageRepositoryImpl.java deleted file mode 100644 index 6dd6c8509..000000000 --- a/backend/ddang/src/main/java/com/ddang/ddang/chat/infrastructure/persistence/QuerydslChatRoomAndMessageAndImageRepositoryImpl.java +++ /dev/null @@ -1,74 +0,0 @@ -package com.ddang.ddang.chat.infrastructure.persistence; - -import com.ddang.ddang.chat.infrastructure.persistence.dto.ChatRoomAndMessageAndImageDto; -import com.ddang.ddang.chat.infrastructure.persistence.dto.ChatRoomAndMessageAndImageQueryProjectionDto; -import com.ddang.ddang.chat.infrastructure.persistence.dto.QChatRoomAndMessageAndImageQueryProjectionDto; -import com.querydsl.core.types.dsl.BooleanExpression; -import com.querydsl.jpa.JPAExpressions; -import com.querydsl.jpa.impl.JPAQueryFactory; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Repository; - -import java.util.List; -import java.util.Objects; - -import static com.ddang.ddang.auction.domain.QAuction.auction; -import static com.ddang.ddang.chat.domain.QChatRoom.chatRoom; -import static com.ddang.ddang.chat.domain.QMessage.message; -import static com.ddang.ddang.image.domain.QAuctionImage.auctionImage; -import static java.util.Comparator.comparing; - -@Repository -@RequiredArgsConstructor -public class QuerydslChatRoomAndMessageAndImageRepositoryImpl implements QuerydslChatRoomAndMessageAndImageRepository { - - private final JPAQueryFactory queryFactory; - - @Override - public List findAllChatRoomInfoByUserIdOrderByLastMessage(final Long userId) { - final List unsortedDtos = - queryFactory.select(new QChatRoomAndMessageAndImageQueryProjectionDto(chatRoom, message, auctionImage)) - .from(chatRoom) - .leftJoin(chatRoom.buyer).fetchJoin() - .leftJoin(chatRoom.auction, auction).fetchJoin() - .leftJoin(auction.seller).fetchJoin() - .leftJoin(auctionImage).on(auctionImage.id.eq( - JPAExpressions - .select(auctionImage.id.min()) - .from(auctionImage) - .where(auctionImage.auction.id.eq(auction.id)) - .groupBy(auctionImage.auction.id) - )).fetchJoin() - .leftJoin(auction.lastBid).fetchJoin() - .leftJoin(message).on(message.id.eq( - JPAExpressions - .select(message.id.max()) - .from(message) - .where(message.chatRoom.id.eq(chatRoom.id)) - .groupBy(message.chatRoom.id) - )).fetchJoin() - .where(isSellerOrWinner(userId)) - .fetch(); - - return sortByLastMessageIdDesc(unsortedDtos); - } - - private List sortByLastMessageIdDesc( - final List unsortedDtos - ) { - return unsortedDtos.stream() - .filter((ChatRoomAndMessageAndImageQueryProjectionDto unsortedDto) -> - Objects.nonNull(unsortedDto.message()) - ).sorted(comparing( - (ChatRoomAndMessageAndImageQueryProjectionDto unsortedDto) -> - unsortedDto.message().getId() - ).reversed() - ).map(ChatRoomAndMessageAndImageQueryProjectionDto::toSortedDto) - .toList(); - } - - private BooleanExpression isSellerOrWinner(final Long userId) { - return (auction.seller.id.eq(userId)) - .or(chatRoom.buyer.id.eq(userId)); - } -} diff --git a/backend/ddang/src/main/java/com/ddang/ddang/chat/infrastructure/persistence/QuerydslChatRoomRepository.java b/backend/ddang/src/main/java/com/ddang/ddang/chat/infrastructure/persistence/QuerydslChatRoomRepository.java deleted file mode 100644 index c789bff0b..000000000 --- a/backend/ddang/src/main/java/com/ddang/ddang/chat/infrastructure/persistence/QuerydslChatRoomRepository.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.ddang.ddang.chat.infrastructure.persistence; - -import java.util.Optional; - -public interface QuerydslChatRoomRepository { - - Optional findChatRoomIdByAuctionId(final Long auctionId); -} diff --git a/backend/ddang/src/main/java/com/ddang/ddang/chat/infrastructure/persistence/QuerydslChatRoomRepositoryImpl.java b/backend/ddang/src/main/java/com/ddang/ddang/chat/infrastructure/persistence/QuerydslChatRoomRepositoryImpl.java deleted file mode 100644 index b3b8908d6..000000000 --- a/backend/ddang/src/main/java/com/ddang/ddang/chat/infrastructure/persistence/QuerydslChatRoomRepositoryImpl.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.ddang.ddang.chat.infrastructure.persistence; - -import com.querydsl.jpa.impl.JPAQueryFactory; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Repository; - -import java.util.Optional; - -import static com.ddang.ddang.chat.domain.QChatRoom.chatRoom; - -@Repository -@RequiredArgsConstructor -public class QuerydslChatRoomRepositoryImpl implements QuerydslChatRoomRepository { - - private final JPAQueryFactory queryFactory; - - @Override - public Optional findChatRoomIdByAuctionId(final Long auctionId) { - final Long chatRoomId = queryFactory.select(chatRoom.id) - .from(chatRoom) - .where(chatRoom.auction.id.eq(auctionId)) - .fetchFirst(); - - return Optional.ofNullable(chatRoomId); - } -} diff --git a/backend/ddang/src/main/java/com/ddang/ddang/chat/infrastructure/persistence/QuerydslMessageRepository.java b/backend/ddang/src/main/java/com/ddang/ddang/chat/infrastructure/persistence/QuerydslMessageRepository.java index ad979d450..3115e135b 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/chat/infrastructure/persistence/QuerydslMessageRepository.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/chat/infrastructure/persistence/QuerydslMessageRepository.java @@ -1,13 +1,43 @@ package com.ddang.ddang.chat.infrastructure.persistence; import com.ddang.ddang.chat.domain.Message; +import com.querydsl.core.types.dsl.BooleanExpression; +import com.querydsl.jpa.impl.JPAQueryFactory; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Repository; import java.util.List; -public interface QuerydslMessageRepository { +import static com.ddang.ddang.chat.domain.QMessage.message; - List findMessagesAllByLastMessageId( +@Repository +@RequiredArgsConstructor +public class QuerydslMessageRepository { + + private final JPAQueryFactory queryFactory; + + public List findAllByLastMessageId( final Long messageReaderId, final Long chatRoomId, - final Long lastMessageId); + final Long lastMessageId + ) { + return queryFactory + .selectFrom(message) + .where( + message.writer.id.eq(messageReaderId) + .or(message.receiver.id.eq(messageReaderId)), + message.chatRoom.id.eq(chatRoomId), + isGreaterThanLastId(lastMessageId) + ) + .orderBy(message.id.asc()) + .fetch(); + } + + private BooleanExpression isGreaterThanLastId(final Long lastMessageId) { + if (lastMessageId == null) { + return null; + } + + return message.id.gt(lastMessageId); + } } diff --git a/backend/ddang/src/main/java/com/ddang/ddang/chat/infrastructure/persistence/QuerydslMessageRepositoryImpl.java b/backend/ddang/src/main/java/com/ddang/ddang/chat/infrastructure/persistence/QuerydslMessageRepositoryImpl.java deleted file mode 100644 index 65b9cfd90..000000000 --- a/backend/ddang/src/main/java/com/ddang/ddang/chat/infrastructure/persistence/QuerydslMessageRepositoryImpl.java +++ /dev/null @@ -1,43 +0,0 @@ -package com.ddang.ddang.chat.infrastructure.persistence; - -import com.ddang.ddang.chat.domain.Message; -import com.querydsl.core.types.dsl.BooleanExpression; -import com.querydsl.jpa.impl.JPAQueryFactory; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Repository; - -import java.util.List; - -import static com.ddang.ddang.chat.domain.QMessage.message; - -@Repository -@RequiredArgsConstructor -public class QuerydslMessageRepositoryImpl implements QuerydslMessageRepository { - - private final JPAQueryFactory queryFactory; - - public List findMessagesAllByLastMessageId( - final Long messageReaderId, - final Long chatRoomId, - final Long lastMessageId - ) { - return queryFactory - .selectFrom(message) - .where( - message.writer.id.eq(messageReaderId) - .or(message.receiver.id.eq(messageReaderId)), - message.chatRoom.id.eq(chatRoomId), - isGreaterThanLastId(lastMessageId) - ) - .orderBy(message.id.asc()) - .fetch(); - } - - private BooleanExpression isGreaterThanLastId(final Long lastMessageId) { - if (lastMessageId == null) { - return null; - } - - return message.id.gt(lastMessageId); - } -} diff --git a/backend/ddang/src/main/java/com/ddang/ddang/chat/infrastructure/persistence/dto/ChatRoomAndImageQueryProjectionDto.java b/backend/ddang/src/main/java/com/ddang/ddang/chat/infrastructure/persistence/dto/ChatRoomAndImageQueryProjectionDto.java index 4e52de7c8..4de8cde25 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/chat/infrastructure/persistence/dto/ChatRoomAndImageQueryProjectionDto.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/chat/infrastructure/persistence/dto/ChatRoomAndImageQueryProjectionDto.java @@ -1,12 +1,12 @@ package com.ddang.ddang.chat.infrastructure.persistence.dto; import com.ddang.ddang.chat.domain.ChatRoom; +import com.ddang.ddang.chat.domain.dto.ChatRoomAndImageDto; import com.ddang.ddang.image.domain.AuctionImage; import com.querydsl.core.annotations.QueryProjection; public record ChatRoomAndImageQueryProjectionDto(ChatRoom chatRoom, AuctionImage auctionImage) { - // TODO: 2023/09/19 네이밍 컨벤션 회의 후 리팩토링 예정 @QueryProjection public ChatRoomAndImageQueryProjectionDto { } diff --git a/backend/ddang/src/main/java/com/ddang/ddang/chat/infrastructure/persistence/dto/ChatRoomAndMessageAndImageQueryProjectionDto.java b/backend/ddang/src/main/java/com/ddang/ddang/chat/infrastructure/persistence/dto/ChatRoomAndMessageAndImageQueryProjectionDto.java index a20e2b827..c4369d8f3 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/chat/infrastructure/persistence/dto/ChatRoomAndMessageAndImageQueryProjectionDto.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/chat/infrastructure/persistence/dto/ChatRoomAndMessageAndImageQueryProjectionDto.java @@ -2,12 +2,12 @@ import com.ddang.ddang.chat.domain.ChatRoom; import com.ddang.ddang.chat.domain.Message; +import com.ddang.ddang.chat.domain.dto.ChatRoomAndMessageAndImageDto; import com.ddang.ddang.image.domain.AuctionImage; import com.querydsl.core.annotations.QueryProjection; public record ChatRoomAndMessageAndImageQueryProjectionDto(ChatRoom chatRoom, Message message, AuctionImage auctionImage) { - // TODO: 2023/09/19 네이밍 컨벤션 회의 후 리팩토링 예정 @QueryProjection public ChatRoomAndMessageAndImageQueryProjectionDto { } diff --git a/backend/ddang/src/main/java/com/ddang/ddang/configuration/SchedulingConfiguration.java b/backend/ddang/src/main/java/com/ddang/ddang/configuration/SchedulingConfiguration.java new file mode 100644 index 000000000..e8141a7ca --- /dev/null +++ b/backend/ddang/src/main/java/com/ddang/ddang/configuration/SchedulingConfiguration.java @@ -0,0 +1,9 @@ +package com.ddang.ddang.configuration; + +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.annotation.EnableScheduling; + +@Configuration +@EnableScheduling +public class SchedulingConfiguration { +} diff --git a/backend/ddang/src/main/java/com/ddang/ddang/configuration/initialization/InitializationAuctionConfiguration.java b/backend/ddang/src/main/java/com/ddang/ddang/configuration/initialization/InitializationAuctionConfiguration.java deleted file mode 100644 index bb741693e..000000000 --- a/backend/ddang/src/main/java/com/ddang/ddang/configuration/initialization/InitializationAuctionConfiguration.java +++ /dev/null @@ -1,73 +0,0 @@ -package com.ddang.ddang.configuration.initialization; - -import com.ddang.ddang.auction.domain.Auction; -import com.ddang.ddang.auction.domain.BidUnit; -import com.ddang.ddang.auction.domain.Price; -import com.ddang.ddang.auction.infrastructure.persistence.JpaAuctionRepository; -import com.ddang.ddang.category.domain.Category; -import com.ddang.ddang.category.infrastructure.persistence.JpaCategoryRepository; -import com.ddang.ddang.region.domain.AuctionRegion; -import com.ddang.ddang.region.domain.Region; -import com.ddang.ddang.region.infrastructure.persistence.JpaRegionRepository; -import lombok.RequiredArgsConstructor; -import org.springframework.boot.ApplicationArguments; -import org.springframework.boot.ApplicationRunner; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.context.annotation.Configuration; -import org.springframework.transaction.annotation.Transactional; - -import java.time.LocalDateTime; -import java.util.List; - -@Configuration -@ConditionalOnProperty(name = "data.init.auction.enabled", havingValue = "true") -@RequiredArgsConstructor -public class InitializationAuctionConfiguration implements ApplicationRunner { - - private final JpaAuctionRepository auctionRepository; - private final JpaRegionRepository regionRepository; - private final JpaCategoryRepository categoryRepository; - - @Override - @Transactional - public void run(final ApplicationArguments args) { - final Region firstRegion = regionRepository.findById(1L).get(); - final Region secondRegion = regionRepository.findById(2L).get(); - final Region thirdRegion = regionRepository.findById(3L).get(); - - secondRegion.addThirdRegion(thirdRegion); - firstRegion.addSecondRegion(secondRegion); - - final AuctionRegion auctionRegion1 = new AuctionRegion(thirdRegion); - final AuctionRegion auctionRegion2 = new AuctionRegion(thirdRegion); - - final Category main = new Category("전자기기"); - final Category sub = new Category("노트북"); - - main.addSubCategory(sub); - - categoryRepository.save(main); - - final Auction auction1 = Auction.builder() - .title("맥북 2021 13인치") - .description("맥북 2021 13인치 팝니다. 애플 감성 안 맞아요.") - .bidUnit(new BidUnit(1_000)) - .startPrice(new Price(1_000_000)) - .closingTime(LocalDateTime.now()) - .build(); - auction1.addAuctionRegions(List.of(auctionRegion1)); - - final Auction auction2 = Auction.builder() - .title("맥북 16인치") - .description("맥북 2019 16인치 팝니다. 급전이 필요해요...") - .bidUnit(new BidUnit(3_000)) - .startPrice(new Price(900_000)) - .closingTime(LocalDateTime.now() - .plusDays(5L)) - .build(); - auction2.addAuctionRegions(List.of(auctionRegion2)); - - auctionRepository.save(auction1); - auctionRepository.save(auction2); - } -} diff --git a/backend/ddang/src/main/java/com/ddang/ddang/configuration/initialization/InitializationUserConfiguration.java b/backend/ddang/src/main/java/com/ddang/ddang/configuration/initialization/InitializationUserConfiguration.java deleted file mode 100644 index eca2b6d86..000000000 --- a/backend/ddang/src/main/java/com/ddang/ddang/configuration/initialization/InitializationUserConfiguration.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.ddang.ddang.configuration.initialization; - -import com.ddang.ddang.image.domain.ProfileImage; -import com.ddang.ddang.user.domain.Reliability; -import com.ddang.ddang.user.domain.User; -import com.ddang.ddang.user.infrastructure.persistence.JpaUserRepository; -import lombok.RequiredArgsConstructor; -import org.springframework.boot.ApplicationArguments; -import org.springframework.boot.ApplicationRunner; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.context.annotation.Configuration; -import org.springframework.transaction.annotation.Transactional; - -@Configuration -@ConditionalOnProperty(name = "data.init.user.enabled", havingValue = "true") -@RequiredArgsConstructor -public class InitializationUserConfiguration implements ApplicationRunner { - - private final JpaUserRepository userRepository; - - @Override - @Transactional - public void run(final ApplicationArguments args) { - final User seller1 = User.builder() - .name("판매자1") - .profileImage(new ProfileImage("upload.png", "updateImage.png")) - .reliability(new Reliability(4.7d)) - .oauthId("12345") - .build(); - - final User buyer1 = User.builder() - .name("구매자1") - .profileImage(new ProfileImage("upload.png", "updateImage.png")) - .reliability(new Reliability(3.0d)) - .oauthId("12346") - .build(); - - final User buyer2 = User.builder() - .name("구매자2") - .profileImage(new ProfileImage("upload.png", "updateImage.png")) - .reliability(new Reliability(0.8d)) - .oauthId("12347") - .build(); - - userRepository.save(seller1); - userRepository.save(buyer1); - userRepository.save(buyer2); - } -} diff --git a/backend/ddang/src/main/java/com/ddang/ddang/device/application/DeviceTokenService.java b/backend/ddang/src/main/java/com/ddang/ddang/device/application/DeviceTokenService.java index 47ca28e74..3377accda 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/device/application/DeviceTokenService.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/device/application/DeviceTokenService.java @@ -2,10 +2,10 @@ import com.ddang.ddang.device.application.dto.PersistDeviceTokenDto; import com.ddang.ddang.device.domain.DeviceToken; -import com.ddang.ddang.device.infrastructure.persistence.JpaDeviceTokenRepository; +import com.ddang.ddang.device.domain.repository.DeviceTokenRepository; import com.ddang.ddang.user.application.exception.UserNotFoundException; import com.ddang.ddang.user.domain.User; -import com.ddang.ddang.user.infrastructure.persistence.JpaUserRepository; +import com.ddang.ddang.user.domain.repository.UserRepository; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -15,8 +15,8 @@ @RequiredArgsConstructor public class DeviceTokenService { - private final JpaDeviceTokenRepository deviceTokenRepository; - private final JpaUserRepository userRepository; + private final DeviceTokenRepository deviceTokenRepository; + private final UserRepository userRepository; @Transactional public void persist(final Long userId, final PersistDeviceTokenDto deviceTokenDto) { diff --git a/backend/ddang/src/main/java/com/ddang/ddang/device/domain/repository/DeviceTokenRepository.java b/backend/ddang/src/main/java/com/ddang/ddang/device/domain/repository/DeviceTokenRepository.java new file mode 100644 index 000000000..02ce0b505 --- /dev/null +++ b/backend/ddang/src/main/java/com/ddang/ddang/device/domain/repository/DeviceTokenRepository.java @@ -0,0 +1,14 @@ +package com.ddang.ddang.device.domain.repository; + +import com.ddang.ddang.device.domain.DeviceToken; + +import java.util.Optional; + +public interface DeviceTokenRepository { + + DeviceToken save(final DeviceToken deviceToken); + + Optional findByUserId(final Long userId); + + void deleteByUserId(final Long id); +} diff --git a/backend/ddang/src/main/java/com/ddang/ddang/device/infrastructure/persistence/DeviceTokenRepositoryImpl.java b/backend/ddang/src/main/java/com/ddang/ddang/device/infrastructure/persistence/DeviceTokenRepositoryImpl.java new file mode 100644 index 000000000..c9e5248d5 --- /dev/null +++ b/backend/ddang/src/main/java/com/ddang/ddang/device/infrastructure/persistence/DeviceTokenRepositoryImpl.java @@ -0,0 +1,30 @@ +package com.ddang.ddang.device.infrastructure.persistence; + +import com.ddang.ddang.device.domain.DeviceToken; +import com.ddang.ddang.device.domain.repository.DeviceTokenRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Repository; + +import java.util.Optional; + +@Repository +@RequiredArgsConstructor +public class DeviceTokenRepositoryImpl implements DeviceTokenRepository { + + private final JpaDeviceTokenRepository jpaDeviceTokenRepository; + + @Override + public DeviceToken save(final DeviceToken deviceToken) { + return jpaDeviceTokenRepository.save(deviceToken); + } + + @Override + public Optional findByUserId(final Long userId) { + return jpaDeviceTokenRepository.findByUserId(userId); + } + + @Override + public void deleteByUserId(final Long id) { + jpaDeviceTokenRepository.deleteByUserId(id); + } +} diff --git a/backend/ddang/src/main/java/com/ddang/ddang/device/infrastructure/persistence/JpaDeviceTokenRepository.java b/backend/ddang/src/main/java/com/ddang/ddang/device/infrastructure/persistence/JpaDeviceTokenRepository.java index 13fc6f49b..f58182fde 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/device/infrastructure/persistence/JpaDeviceTokenRepository.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/device/infrastructure/persistence/JpaDeviceTokenRepository.java @@ -9,5 +9,5 @@ public interface JpaDeviceTokenRepository extends JpaRepository findByUserId(final Long userId); - void deleteByUserId(Long id); + void deleteByUserId(final Long id); } diff --git a/backend/ddang/src/main/java/com/ddang/ddang/exception/GlobalExceptionHandler.java b/backend/ddang/src/main/java/com/ddang/ddang/exception/GlobalExceptionHandler.java index 9438d22e0..e3c7b2ea0 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/exception/GlobalExceptionHandler.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/exception/GlobalExceptionHandler.java @@ -6,6 +6,7 @@ import com.ddang.ddang.auction.domain.exception.InvalidPriceValueException; import com.ddang.ddang.auction.domain.exception.WinnerNotFoundException; import com.ddang.ddang.authentication.application.exception.InvalidWithdrawalException; +import com.ddang.ddang.authentication.application.exception.WithdrawalNotAllowedException; import com.ddang.ddang.authentication.configuration.exception.UserUnauthorizedException; import com.ddang.ddang.authentication.domain.exception.InvalidTokenException; import com.ddang.ddang.authentication.domain.exception.UnsupportedSocialLoginException; @@ -38,6 +39,7 @@ import com.ddang.ddang.report.application.exception.InvalidReporterToAuctionException; import com.ddang.ddang.review.application.exception.AlreadyReviewException; import com.ddang.ddang.review.application.exception.ReviewNotFoundException; +import com.ddang.ddang.user.application.exception.AlreadyExistsNameException; import com.ddang.ddang.user.application.exception.UserNotFoundException; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; @@ -409,6 +411,22 @@ public ResponseEntity handleAlreadyAnsweredException(final Al .body(new ExceptionResponse(ex.getMessage())); } + @ExceptionHandler(AlreadyExistsNameException.class) + public ResponseEntity handleAlreadyExistsNameException(final AlreadyExistsNameException ex) { + logger.warn(String.format(LOG_MESSAGE_FORMAT, ex.getClass().getSimpleName(), ex.getMessage())); + + return ResponseEntity.status(HttpStatus.BAD_REQUEST) + .body(new ExceptionResponse(ex.getMessage())); + } + + @ExceptionHandler(WithdrawalNotAllowedException.class) + public ResponseEntity handleWithdrawalNotAllowedException(final WithdrawalNotAllowedException ex) { + logger.warn(String.format(LOG_MESSAGE_FORMAT, ex.getClass().getSimpleName(), ex.getMessage())); + + return ResponseEntity.status(HttpStatus.BAD_REQUEST) + .body(new ExceptionResponse(ex.getMessage())); + } + @Override protected ResponseEntity handleMethodArgumentNotValid( final MethodArgumentNotValidException ex, diff --git a/backend/ddang/src/main/java/com/ddang/ddang/image/application/ImageService.java b/backend/ddang/src/main/java/com/ddang/ddang/image/application/ImageService.java index bb64c5dca..7b9f240e3 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/image/application/ImageService.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/image/application/ImageService.java @@ -2,8 +2,8 @@ import com.ddang.ddang.image.domain.AuctionImage; import com.ddang.ddang.image.domain.ProfileImage; -import com.ddang.ddang.image.infrastructure.persistence.JpaAuctionImageRepository; -import com.ddang.ddang.image.infrastructure.persistence.JpaProfileImageRepository; +import com.ddang.ddang.image.domain.repository.AuctionImageRepository; +import com.ddang.ddang.image.domain.repository.ProfileImageRepository; import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Value; import org.springframework.core.io.Resource; @@ -22,12 +22,12 @@ public class ImageService { @Value("${image.store.dir}") private String imageStoreDir; - private final JpaProfileImageRepository imageRepository; - private final JpaAuctionImageRepository auctionImageRepository; + private final ProfileImageRepository profileImageRepository; + private final AuctionImageRepository auctionImageRepository; public Resource readProfileImage(final Long id) throws MalformedURLException { - final ProfileImage profileImage = imageRepository.findById(id) - .orElse(null); + final ProfileImage profileImage = profileImageRepository.findById(id) + .orElse(null); if (profileImage == null) { return null; diff --git a/backend/ddang/src/main/java/com/ddang/ddang/image/domain/AuctionImage.java b/backend/ddang/src/main/java/com/ddang/ddang/image/domain/AuctionImage.java index dcc81b34b..a937fd272 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/image/domain/AuctionImage.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/image/domain/AuctionImage.java @@ -21,7 +21,6 @@ @Getter @EqualsAndHashCode(of = "id") @ToString(of = {"id", "image", "authenticated"}) -// TODO: 9/29/23 추후 대표 이미지 구분을 위한 필드 추가 public class AuctionImage { @Id diff --git a/backend/ddang/src/main/java/com/ddang/ddang/image/domain/ProfileImage.java b/backend/ddang/src/main/java/com/ddang/ddang/image/domain/ProfileImage.java index e45936a8c..d85c01c1e 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/image/domain/ProfileImage.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/image/domain/ProfileImage.java @@ -19,6 +19,8 @@ public class ProfileImage { public static final String DEFAULT_PROFILE_IMAGE_STORE_NAME = "default_profile_image.png"; + // TODO: 10/13/23 앞으로 id가 아닌 store name으로 진행하기로 했는데, 임시로 해둡니다. 추후 삭제해주시면 감사하겠습니다. + public static final String DEFAULT_PROFILE_IMAGE_ID = "1"; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) diff --git a/backend/ddang/src/main/java/com/ddang/ddang/image/domain/repository/AuctionImageRepository.java b/backend/ddang/src/main/java/com/ddang/ddang/image/domain/repository/AuctionImageRepository.java new file mode 100644 index 000000000..586bca166 --- /dev/null +++ b/backend/ddang/src/main/java/com/ddang/ddang/image/domain/repository/AuctionImageRepository.java @@ -0,0 +1,10 @@ +package com.ddang.ddang.image.domain.repository; + +import com.ddang.ddang.image.domain.AuctionImage; + +import java.util.Optional; + +public interface AuctionImageRepository { + + Optional findById(final Long id); +} diff --git a/backend/ddang/src/main/java/com/ddang/ddang/image/domain/repository/ProfileImageRepository.java b/backend/ddang/src/main/java/com/ddang/ddang/image/domain/repository/ProfileImageRepository.java new file mode 100644 index 000000000..b0491a5d6 --- /dev/null +++ b/backend/ddang/src/main/java/com/ddang/ddang/image/domain/repository/ProfileImageRepository.java @@ -0,0 +1,12 @@ +package com.ddang.ddang.image.domain.repository; + +import com.ddang.ddang.image.domain.ProfileImage; + +import java.util.Optional; + +public interface ProfileImageRepository { + + Optional findById(final Long id); + + Optional findByStoreName(final String storeName); +} diff --git a/backend/ddang/src/main/java/com/ddang/ddang/image/infrastructure/persistence/AuctionImageRepositoryImpl.java b/backend/ddang/src/main/java/com/ddang/ddang/image/infrastructure/persistence/AuctionImageRepositoryImpl.java new file mode 100644 index 000000000..c24799bca --- /dev/null +++ b/backend/ddang/src/main/java/com/ddang/ddang/image/infrastructure/persistence/AuctionImageRepositoryImpl.java @@ -0,0 +1,20 @@ +package com.ddang.ddang.image.infrastructure.persistence; + +import com.ddang.ddang.image.domain.AuctionImage; +import com.ddang.ddang.image.domain.repository.AuctionImageRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Repository; + +import java.util.Optional; + +@Repository +@RequiredArgsConstructor +public class AuctionImageRepositoryImpl implements AuctionImageRepository { + + private final JpaAuctionImageRepository jpaAuctionImageRepository; + + @Override + public Optional findById(final Long id) { + return jpaAuctionImageRepository.findById(id); + } +} diff --git a/backend/ddang/src/main/java/com/ddang/ddang/image/infrastructure/persistence/JpaAuctionImageRepository.java b/backend/ddang/src/main/java/com/ddang/ddang/image/infrastructure/persistence/JpaAuctionImageRepository.java index da66d1e6a..87f345983 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/image/infrastructure/persistence/JpaAuctionImageRepository.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/image/infrastructure/persistence/JpaAuctionImageRepository.java @@ -4,5 +4,4 @@ import org.springframework.data.jpa.repository.JpaRepository; public interface JpaAuctionImageRepository extends JpaRepository { - } diff --git a/backend/ddang/src/main/java/com/ddang/ddang/image/infrastructure/persistence/JpaProfileImageRepository.java b/backend/ddang/src/main/java/com/ddang/ddang/image/infrastructure/persistence/JpaProfileImageRepository.java index f30774152..39b321bad 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/image/infrastructure/persistence/JpaProfileImageRepository.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/image/infrastructure/persistence/JpaProfileImageRepository.java @@ -8,6 +8,6 @@ public interface JpaProfileImageRepository extends JpaRepository { - @Query("select i from ProfileImage i where i.image.storeName = :storeName") + @Query("SELECT i FROM ProfileImage i WHERE i.image.storeName = :storeName") Optional findByStoreName(final String storeName); } diff --git a/backend/ddang/src/main/java/com/ddang/ddang/image/infrastructure/persistence/ProfileImageRepositoryImpl.java b/backend/ddang/src/main/java/com/ddang/ddang/image/infrastructure/persistence/ProfileImageRepositoryImpl.java new file mode 100644 index 000000000..12fa367f7 --- /dev/null +++ b/backend/ddang/src/main/java/com/ddang/ddang/image/infrastructure/persistence/ProfileImageRepositoryImpl.java @@ -0,0 +1,25 @@ +package com.ddang.ddang.image.infrastructure.persistence; + +import com.ddang.ddang.image.domain.ProfileImage; +import com.ddang.ddang.image.domain.repository.ProfileImageRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Repository; + +import java.util.Optional; + +@Repository +@RequiredArgsConstructor +public class ProfileImageRepositoryImpl implements ProfileImageRepository { + + private final JpaProfileImageRepository jpaProfileImageRepository; + + @Override + public Optional findById(final Long id) { + return jpaProfileImageRepository.findById(id); + } + + @Override + public Optional findByStoreName(final String storeName) { + return jpaProfileImageRepository.findByStoreName(storeName); + } +} diff --git a/backend/ddang/src/main/java/com/ddang/ddang/image/presentation/util/ImageRelativeUrl.java b/backend/ddang/src/main/java/com/ddang/ddang/image/presentation/util/ImageRelativeUrl.java index 7fa5603ea..854b6f813 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/image/presentation/util/ImageRelativeUrl.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/image/presentation/util/ImageRelativeUrl.java @@ -20,4 +20,8 @@ public String calculateAbsoluteUrl() { return imageBaseUrl + value; } + + public String getValue() { + return value; + } } diff --git a/backend/ddang/src/main/java/com/ddang/ddang/image/presentation/util/ImageUrlCalculator.java b/backend/ddang/src/main/java/com/ddang/ddang/image/presentation/util/ImageUrlCalculator.java index f2a0b8759..374a03459 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/image/presentation/util/ImageUrlCalculator.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/image/presentation/util/ImageUrlCalculator.java @@ -1,18 +1,27 @@ package com.ddang.ddang.image.presentation.util; +import com.ddang.ddang.image.domain.ProfileImage; + public final class ImageUrlCalculator { private ImageUrlCalculator() { } - // TODO: 9/29/23 id 타입을 long으로 변경 및 이미지는 null이 되는 경우가 없도록 할 것 public static String calculateBy(final ImageRelativeUrl imageRelativeUrl, final Long id) { final String absoluteUrl = imageRelativeUrl.calculateAbsoluteUrl(); + if (id == null && imageRelativeUrl == ImageRelativeUrl.USER) { + return absoluteUrl + ProfileImage.DEFAULT_PROFILE_IMAGE_ID; + } + return absoluteUrl + id; } public static String calculateBy(final String imageAbsoluteUrl, final Long id) { + if (id == null && imageAbsoluteUrl.contains(ImageRelativeUrl.USER.getValue())) { + return imageAbsoluteUrl + ProfileImage.DEFAULT_PROFILE_IMAGE_ID; + } + return imageAbsoluteUrl + id; } } diff --git a/backend/ddang/src/main/java/com/ddang/ddang/notification/application/FcmNotificationService.java b/backend/ddang/src/main/java/com/ddang/ddang/notification/application/FcmNotificationService.java index 8eeb9b6ef..78c8068c4 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/notification/application/FcmNotificationService.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/notification/application/FcmNotificationService.java @@ -2,7 +2,7 @@ import com.ddang.ddang.device.application.exception.DeviceTokenNotFoundException; import com.ddang.ddang.device.domain.DeviceToken; -import com.ddang.ddang.device.infrastructure.persistence.JpaDeviceTokenRepository; +import com.ddang.ddang.device.domain.repository.DeviceTokenRepository; import com.ddang.ddang.notification.application.dto.CreateNotificationDto; import com.ddang.ddang.notification.domain.NotificationStatus; import com.google.firebase.messaging.AndroidConfig; @@ -27,7 +27,7 @@ public class FcmNotificationService implements NotificationService { private final FirebaseMessaging firebaseMessaging; - private final JpaDeviceTokenRepository deviceTokenRepository; + private final DeviceTokenRepository deviceTokenRepository; @Override public NotificationStatus send(final CreateNotificationDto createNotificationDto) throws FirebaseMessagingException { diff --git a/backend/ddang/src/main/java/com/ddang/ddang/notification/application/NotificationEventListener.java b/backend/ddang/src/main/java/com/ddang/ddang/notification/application/NotificationEventListener.java index d75990490..1a06f1125 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/notification/application/NotificationEventListener.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/notification/application/NotificationEventListener.java @@ -3,13 +3,17 @@ import com.ddang.ddang.auction.domain.Auction; import com.ddang.ddang.bid.application.dto.BidDto; import com.ddang.ddang.bid.application.event.BidNotificationEvent; -import com.ddang.ddang.chat.application.dto.MessageDto; import com.ddang.ddang.chat.application.event.MessageNotificationEvent; +import com.ddang.ddang.chat.domain.Message; import com.ddang.ddang.image.domain.AuctionImage; import com.ddang.ddang.image.domain.ProfileImage; import com.ddang.ddang.image.presentation.util.ImageUrlCalculator; import com.ddang.ddang.notification.application.dto.CreateNotificationDto; import com.ddang.ddang.notification.domain.NotificationType; +import com.ddang.ddang.qna.application.event.AnswerNotificationEvent; +import com.ddang.ddang.qna.application.event.QuestionNotificationEvent; +import com.ddang.ddang.qna.domain.Answer; +import com.ddang.ddang.qna.domain.Question; import com.google.firebase.messaging.FirebaseMessagingException; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -23,23 +27,24 @@ public class NotificationEventListener { private static final String URI_DELIMITER = "/"; private static final String MESSAGE_NOTIFICATION_REDIRECT_URI = "/chattings"; - private static final String BID_NOTIFICATION_REDIRECT_URI = "/auctions"; + private static final String AUCTION_DETAIL_URI = "/auctions"; private static final String BID_NOTIFICATION_MESSAGE_FORMAT = "상위 입찰자가 나타났습니다. 구매를 원하신다면 더 높은 가격을 제시해 주세요."; + private static final int FIRST_IMAGE_INDEX = 0; private final NotificationService notificationService; @TransactionalEventListener public void sendMessageNotification(final MessageNotificationEvent messageNotificationEvent) { try { - final MessageDto messageDto = messageNotificationEvent.messageDto(); - final ProfileImage profileImage = messageDto.receiver().getProfileImage(); + final Message message = messageNotificationEvent.message(); + final ProfileImage profileImage = message.getWriter().getProfileImage(); final CreateNotificationDto createNotificationDto = new CreateNotificationDto( NotificationType.MESSAGE, - messageDto.receiver().getId(), - messageDto.writer().getName(), - messageDto.contents(), - calculateRedirectUrl(MESSAGE_NOTIFICATION_REDIRECT_URI, messageDto.chatRoom().getId()), - ImageUrlCalculator.calculateBy(messageDto.profileImageAbsoluteUrl(), profileImage.getId()) + message.getReceiver().getId(), + message.getWriter().getName(), + message.getContents(), + calculateRedirectUrl(MESSAGE_NOTIFICATION_REDIRECT_URI, message.getChatRoom().getId()), + ImageUrlCalculator.calculateBy(messageNotificationEvent.profileImageAbsoluteUrl(), profileImage.getId()) ); notificationService.send(createNotificationDto); } catch (final FirebaseMessagingException ex) { @@ -58,7 +63,7 @@ public void sendBidNotification(final BidNotificationEvent bidNotificationEvent) bidDto.previousBidderId(), auction.getTitle(), BID_NOTIFICATION_MESSAGE_FORMAT, - calculateRedirectUrl(BID_NOTIFICATION_REDIRECT_URI, auction.getId()), + calculateRedirectUrl(AUCTION_DETAIL_URI, auction.getId()), ImageUrlCalculator.calculateBy(bidDto.auctionImageAbsoluteUrl(), auctionImage.getId()) ); notificationService.send(createNotificationDto); @@ -67,6 +72,47 @@ public void sendBidNotification(final BidNotificationEvent bidNotificationEvent) } } + @TransactionalEventListener + public void sendQuestionNotification(final QuestionNotificationEvent questionNotificationEvent) { + try { + final Question question = questionNotificationEvent.question(); + final Auction auction = question.getAuction(); + final AuctionImage auctionImage = auction.getAuctionImages().get(FIRST_IMAGE_INDEX); + final CreateNotificationDto createNotificationDto = new CreateNotificationDto( + NotificationType.QUESTION, + auction.getSeller().getId(), + auction.getTitle(), + question.getContent(), + calculateRedirectUrl(AUCTION_DETAIL_URI, auction.getId()), + ImageUrlCalculator.calculateBy(questionNotificationEvent.absoluteImageUrl(), auctionImage.getId()) + ); + notificationService.send(createNotificationDto); + } catch (final FirebaseMessagingException ex) { + log.error("exception type : {}, ", ex.getClass().getSimpleName(), ex); + } + } + + @TransactionalEventListener + public void sendAnswerNotification(final AnswerNotificationEvent answerNotificationEvent) { + try { + final Answer answer = answerNotificationEvent.answer(); + final Auction auction = answer.getQuestion().getAuction(); + final Question question = answer.getQuestion(); + final AuctionImage auctionImage = auction.getAuctionImages().get(FIRST_IMAGE_INDEX); + final CreateNotificationDto createNotificationDto = new CreateNotificationDto( + NotificationType.ANSWER, + question.getWriter().getId(), + question.getContent(), + answer.getContent(), + calculateRedirectUrl(AUCTION_DETAIL_URI, auction.getId()), + ImageUrlCalculator.calculateBy(answerNotificationEvent.absoluteImageUrl(), auctionImage.getId()) + ); + notificationService.send(createNotificationDto); + } catch (final FirebaseMessagingException ex) { + log.error("exception type : {}, ", ex.getClass().getSimpleName(), ex); + } + } + private String calculateRedirectUrl(final String uri, final Long id) { return uri + URI_DELIMITER + id; } diff --git a/backend/ddang/src/main/java/com/ddang/ddang/notification/application/util/NotificationProperty.java b/backend/ddang/src/main/java/com/ddang/ddang/notification/application/util/NotificationProperty.java index 7b8c8ac3a..326cc2082 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/notification/application/util/NotificationProperty.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/notification/application/util/NotificationProperty.java @@ -2,7 +2,6 @@ import lombok.Getter; -// TODO: 2023/09/30 안드로이드분들께 image -> imageUrl로 변경 가능한지 여쭤보기 @Getter public enum NotificationProperty { diff --git a/backend/ddang/src/main/java/com/ddang/ddang/notification/domain/NotificationType.java b/backend/ddang/src/main/java/com/ddang/ddang/notification/domain/NotificationType.java index ba0e6ae1e..c40995b37 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/notification/domain/NotificationType.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/notification/domain/NotificationType.java @@ -6,7 +6,9 @@ public enum NotificationType { MESSAGE("message"), - BID("bid"); + BID("bid"), + QUESTION("question"), + ANSWER("answer"); private final String value; diff --git a/backend/ddang/src/main/java/com/ddang/ddang/qna/application/AnswerService.java b/backend/ddang/src/main/java/com/ddang/ddang/qna/application/AnswerService.java index cf1055b8a..c52939eb6 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/qna/application/AnswerService.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/qna/application/AnswerService.java @@ -2,18 +2,20 @@ import com.ddang.ddang.auction.application.exception.UserForbiddenException; import com.ddang.ddang.qna.application.dto.CreateAnswerDto; +import com.ddang.ddang.qna.application.event.AnswerNotificationEvent; import com.ddang.ddang.qna.application.exception.AlreadyAnsweredException; import com.ddang.ddang.qna.application.exception.AnswerNotFoundException; import com.ddang.ddang.qna.application.exception.InvalidAnswererException; import com.ddang.ddang.qna.application.exception.QuestionNotFoundException; import com.ddang.ddang.qna.domain.Answer; import com.ddang.ddang.qna.domain.Question; -import com.ddang.ddang.qna.infrastructure.JpaAnswerRepository; -import com.ddang.ddang.qna.infrastructure.JpaQuestionRepository; +import com.ddang.ddang.qna.domain.repository.AnswerRepository; +import com.ddang.ddang.qna.domain.repository.QuestionRepository; import com.ddang.ddang.user.application.exception.UserNotFoundException; import com.ddang.ddang.user.domain.User; -import com.ddang.ddang.user.infrastructure.persistence.JpaUserRepository; +import com.ddang.ddang.user.domain.repository.UserRepository; import lombok.RequiredArgsConstructor; +import org.springframework.context.ApplicationEventPublisher; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -22,12 +24,13 @@ @RequiredArgsConstructor public class AnswerService { - private final JpaUserRepository userRepository; - private final JpaQuestionRepository questionRepository; - private final JpaAnswerRepository answerRepository; + private final ApplicationEventPublisher answerEventPublisher; + private final UserRepository userRepository; + private final QuestionRepository questionRepository; + private final AnswerRepository answerRepository; @Transactional - public Long create(final CreateAnswerDto answerDto) { + public Long create(final CreateAnswerDto answerDto, final String absoluteImageUrl) { final User writer = userRepository.findById(answerDto.userId()) .orElseThrow(() -> new UserNotFoundException("해당 사용자를 찾을 수 없습니다.")); final Question question = questionRepository.findById(answerDto.questionId()) @@ -38,11 +41,14 @@ public Long create(final CreateAnswerDto answerDto) { checkInvalidAnswerer(question, writer); checkAlreadyAnswered(question); - final Answer answer = answerDto.toEntity(); + final Answer answer = answerDto.toEntity(writer); question.addAnswer(answer); - return answerRepository.save(answer) - .getId(); + final Answer persistAnswer = answerRepository.save(answer); + + answerEventPublisher.publishEvent(new AnswerNotificationEvent(persistAnswer, absoluteImageUrl)); + + return persistAnswer.getId(); } private void checkInvalidAnswerer(final Question question, final User writer) { @@ -59,9 +65,9 @@ private void checkAlreadyAnswered(final Question question) { @Transactional public void deleteById(final Long answerId, final Long userId) { - final Answer answer = answerRepository.findByIdAndDeletedIsFalse(answerId) + final Answer answer = answerRepository.findById(answerId) .orElseThrow(() -> new AnswerNotFoundException("해당 답변을 찾을 수 없습니다.")); - final User user = userRepository.findByIdAndDeletedIsFalse(userId) + final User user = userRepository.findById(userId) .orElseThrow(() -> new UserNotFoundException("해당 사용자를 찾을 수 없습니다.")); if (!answer.isWriter(user)) { diff --git a/backend/ddang/src/main/java/com/ddang/ddang/qna/application/QuestionService.java b/backend/ddang/src/main/java/com/ddang/ddang/qna/application/QuestionService.java index e2462238f..1fb03bbf3 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/qna/application/QuestionService.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/qna/application/QuestionService.java @@ -3,18 +3,20 @@ import com.ddang.ddang.auction.application.exception.AuctionNotFoundException; import com.ddang.ddang.auction.application.exception.UserForbiddenException; import com.ddang.ddang.auction.domain.Auction; -import com.ddang.ddang.auction.infrastructure.persistence.JpaAuctionRepository; +import com.ddang.ddang.auction.domain.repository.AuctionRepository; import com.ddang.ddang.qna.application.dto.CreateQuestionDto; import com.ddang.ddang.qna.application.dto.ReadQnasDto; +import com.ddang.ddang.qna.application.event.QuestionNotificationEvent; import com.ddang.ddang.qna.application.exception.InvalidAuctionToAskQuestionException; import com.ddang.ddang.qna.application.exception.InvalidQuestionerException; import com.ddang.ddang.qna.application.exception.QuestionNotFoundException; import com.ddang.ddang.qna.domain.Question; -import com.ddang.ddang.qna.infrastructure.JpaQuestionRepository; +import com.ddang.ddang.qna.domain.repository.QuestionRepository; import com.ddang.ddang.user.application.exception.UserNotFoundException; import com.ddang.ddang.user.domain.User; -import com.ddang.ddang.user.infrastructure.persistence.JpaUserRepository; +import com.ddang.ddang.user.domain.repository.UserRepository; import lombok.RequiredArgsConstructor; +import org.springframework.context.ApplicationEventPublisher; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -26,15 +28,16 @@ @RequiredArgsConstructor public class QuestionService { - private final JpaAuctionRepository auctionRepository; - private final JpaUserRepository userRepository; - private final JpaQuestionRepository questionRepository; + private final ApplicationEventPublisher questionEventPublisher; + private final AuctionRepository auctionRepository; + private final UserRepository userRepository; + private final QuestionRepository questionRepository; @Transactional - public Long create(final CreateQuestionDto questionDto) { - final User questioner = userRepository.findByIdAndDeletedIsFalse(questionDto.userId()) + public Long create(final CreateQuestionDto questionDto, final String absoluteImageUrl) { + final User questioner = userRepository.findById(questionDto.userId()) .orElseThrow(() -> new UserNotFoundException("해당 사용자를 찾을 수 없습니다.")); - final Auction auction = auctionRepository.findByIdAndDeletedIsFalse(questionDto.auctionId()) + final Auction auction = auctionRepository.findPureAuctionById(questionDto.auctionId()) .orElseThrow(() -> new AuctionNotFoundException("해당 경매를 찾을 수 없습니다.")); checkInvalidAuction(auction); @@ -42,8 +45,11 @@ public Long create(final CreateQuestionDto questionDto) { final Question question = questionDto.toEntity(auction, questioner); - return questionRepository.save(question) - .getId(); + final Question persistQuestion = questionRepository.save(question); + + questionEventPublisher.publishEvent(new QuestionNotificationEvent(persistQuestion, absoluteImageUrl)); + + return persistQuestion.getId(); } private void checkInvalidAuction(final Auction auction) { @@ -58,21 +64,23 @@ private void checkInvalidQuestioner(final Auction auction, final User questioner } } - public ReadQnasDto readAllByAuctionId(final Long auctionId) { + public ReadQnasDto readAllByAuctionId(final Long auctionId, final Long userId) { if (!auctionRepository.existsById(auctionId)) { throw new AuctionNotFoundException("해당 경매를 찾을 수 없습니다."); } + final User user = userRepository.findById(userId) + .orElse(User.EMPTY_USER); final List questions = questionRepository.findAllByAuctionId(auctionId); - return ReadQnasDto.from(questions); + return ReadQnasDto.of(questions, user); } @Transactional public void deleteById(final Long questionId, final Long userId) { final Question question = questionRepository.findById(questionId) .orElseThrow(() -> new QuestionNotFoundException("해당 질문을 찾을 수 없습니다.")); - final User user = userRepository.findByIdAndDeletedIsFalse(userId) + final User user = userRepository.findById(userId) .orElseThrow(() -> new UserNotFoundException("해당 사용자를 찾을 수 없습니다.")); if (!question.isWriter(user)) { diff --git a/backend/ddang/src/main/java/com/ddang/ddang/qna/application/dto/CreateAnswerDto.java b/backend/ddang/src/main/java/com/ddang/ddang/qna/application/dto/CreateAnswerDto.java index 79bf745ba..ec0188c58 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/qna/application/dto/CreateAnswerDto.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/qna/application/dto/CreateAnswerDto.java @@ -2,6 +2,7 @@ import com.ddang.ddang.qna.presentation.dto.request.CreateAnswerRequest; import com.ddang.ddang.qna.domain.Answer; +import com.ddang.ddang.user.domain.User; public record CreateAnswerDto(Long questionId, String content, Long userId) { @@ -9,7 +10,7 @@ public static CreateAnswerDto of(final Long questionId, final CreateAnswerReques return new CreateAnswerDto(questionId, answerRequest.content(), userId); } - public Answer toEntity() { - return new Answer(content); + public Answer toEntity(final User writer) { + return new Answer(writer, content); } } diff --git a/backend/ddang/src/main/java/com/ddang/ddang/qna/application/dto/ReadAnswerDto.java b/backend/ddang/src/main/java/com/ddang/ddang/qna/application/dto/ReadAnswerDto.java index d7739bf11..01b576735 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/qna/application/dto/ReadAnswerDto.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/qna/application/dto/ReadAnswerDto.java @@ -9,14 +9,17 @@ public record ReadAnswerDto( Long id, ReadUserInQnaDto writerDto, String content, - LocalDateTime createdTime + LocalDateTime createdTime, + boolean isDeleted ) { + public static ReadAnswerDto from(final Answer answer, final User writer) { return new ReadAnswerDto( answer.getId(), ReadUserInQnaDto.from(writer), answer.getContent(), - answer.getCreatedTime() + answer.getCreatedTime(), + answer.isDeleted() ); } } diff --git a/backend/ddang/src/main/java/com/ddang/ddang/qna/application/dto/ReadQnaDto.java b/backend/ddang/src/main/java/com/ddang/ddang/qna/application/dto/ReadQnaDto.java index 4b99a000a..2927b3fbe 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/qna/application/dto/ReadQnaDto.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/qna/application/dto/ReadQnaDto.java @@ -1,14 +1,15 @@ package com.ddang.ddang.qna.application.dto; import com.ddang.ddang.qna.domain.Question; +import com.ddang.ddang.user.domain.User; public record ReadQnaDto( ReadQuestionDto readQuestionDto, ReadAnswerDto readAnswerDto ) { - public static ReadQnaDto from(final Question question) { - final ReadQuestionDto readQuestionDto = ReadQuestionDto.from(question); + public static ReadQnaDto of(final Question question, final User user) { + final ReadQuestionDto readQuestionDto = ReadQuestionDto.of(question, user); final ReadAnswerDto readAnswerDto = processReadAnswerDto(question); return new ReadQnaDto(readQuestionDto, readAnswerDto); diff --git a/backend/ddang/src/main/java/com/ddang/ddang/qna/application/dto/ReadQnasDto.java b/backend/ddang/src/main/java/com/ddang/ddang/qna/application/dto/ReadQnasDto.java index 92d776a5a..ba5d72c85 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/qna/application/dto/ReadQnasDto.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/qna/application/dto/ReadQnasDto.java @@ -1,14 +1,15 @@ package com.ddang.ddang.qna.application.dto; import com.ddang.ddang.qna.domain.Question; +import com.ddang.ddang.user.domain.User; import java.util.List; public record ReadQnasDto(List readQnaDtos) { - public static ReadQnasDto from(final List questions) { + public static ReadQnasDto of(final List questions, final User user) { final List readQnaDtos = questions.stream() - .map(ReadQnaDto::from) + .map(question -> ReadQnaDto.of(question, user)) .toList(); return new ReadQnasDto(readQnaDtos); diff --git a/backend/ddang/src/main/java/com/ddang/ddang/qna/application/dto/ReadQuestionDto.java b/backend/ddang/src/main/java/com/ddang/ddang/qna/application/dto/ReadQuestionDto.java index edca5530e..6f13fe347 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/qna/application/dto/ReadQuestionDto.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/qna/application/dto/ReadQuestionDto.java @@ -1,6 +1,7 @@ package com.ddang.ddang.qna.application.dto; import com.ddang.ddang.qna.domain.Question; +import com.ddang.ddang.user.domain.User; import java.time.LocalDateTime; @@ -8,15 +9,29 @@ public record ReadQuestionDto( Long id, ReadUserInQnaDto readUserInQnaDto, String content, - LocalDateTime createdTime + LocalDateTime createdTime, + boolean isDeleted, + boolean isQuestioner ) { - public static ReadQuestionDto from(final Question question) { + private static final boolean IS_NOT_WRITER = false; + + public static ReadQuestionDto of(final Question question, final User user) { return new ReadQuestionDto( question.getId(), ReadUserInQnaDto.from(question.getWriter()), question.getContent(), - question.getCreatedTime() + question.getCreatedTime(), + question.isDeleted(), + isWriter(question, user) ); } + + private static boolean isWriter(final Question question, final User user) { + if (user == User.EMPTY_USER) { + return IS_NOT_WRITER; + } + + return question.isWriter(user); + } } diff --git a/backend/ddang/src/main/java/com/ddang/ddang/qna/application/dto/ReadUserInQnaDto.java b/backend/ddang/src/main/java/com/ddang/ddang/qna/application/dto/ReadUserInQnaDto.java index d0a6b2812..a75d44558 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/qna/application/dto/ReadUserInQnaDto.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/qna/application/dto/ReadUserInQnaDto.java @@ -16,7 +16,7 @@ public static ReadUserInQnaDto from(final User writer) { writer.getName(), writer.getProfileImage().getId(), writer.getReliability().getValue(), - writer.getOauthId(), + writer.getOauthInformation().getOauthId(), writer.isDeleted() ); } diff --git a/backend/ddang/src/main/java/com/ddang/ddang/qna/application/event/AnswerNotificationEvent.java b/backend/ddang/src/main/java/com/ddang/ddang/qna/application/event/AnswerNotificationEvent.java new file mode 100644 index 000000000..b960cc420 --- /dev/null +++ b/backend/ddang/src/main/java/com/ddang/ddang/qna/application/event/AnswerNotificationEvent.java @@ -0,0 +1,6 @@ +package com.ddang.ddang.qna.application.event; + +import com.ddang.ddang.qna.domain.Answer; + +public record AnswerNotificationEvent(Answer answer, String absoluteImageUrl) { +} diff --git a/backend/ddang/src/main/java/com/ddang/ddang/qna/application/event/QuestionNotificationEvent.java b/backend/ddang/src/main/java/com/ddang/ddang/qna/application/event/QuestionNotificationEvent.java new file mode 100644 index 000000000..544e1b0cc --- /dev/null +++ b/backend/ddang/src/main/java/com/ddang/ddang/qna/application/event/QuestionNotificationEvent.java @@ -0,0 +1,6 @@ +package com.ddang.ddang.qna.application.event; + +import com.ddang.ddang.qna.domain.Question; + +public record QuestionNotificationEvent(Question question, String absoluteImageUrl) { +} diff --git a/backend/ddang/src/main/java/com/ddang/ddang/qna/domain/Answer.java b/backend/ddang/src/main/java/com/ddang/ddang/qna/domain/Answer.java index d21924f5a..d22dadec0 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/qna/domain/Answer.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/qna/domain/Answer.java @@ -10,6 +10,7 @@ import jakarta.persistence.GenerationType; import jakarta.persistence.Id; import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; import jakarta.persistence.OneToOne; import lombok.AccessLevel; import lombok.EqualsAndHashCode; @@ -25,11 +26,16 @@ public class Answer extends BaseCreateTimeEntity { private static final boolean DELETED_STATUS = true; + private static final Question EMPTY_QUESTION = null; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "writer_id", foreignKey = @ForeignKey(name = "fk_answer_writer")) + private User writer; + @OneToOne(fetch = FetchType.LAZY) @JoinColumn(name = "question_id", foreignKey = @ForeignKey(name = "fk_answer_question")) private Question question; @@ -40,7 +46,8 @@ public class Answer extends BaseCreateTimeEntity { @Column(name = "is_deleted") private boolean deleted = false; - public Answer(final String content) { + public Answer(final User writer, final String content) { + this.writer = writer; this.content = content; } @@ -49,14 +56,15 @@ public void initQuestion(final Question question) { } public boolean isWriter(final User user) { - return question.getAuction().isOwner(user); + return writer.equals(user); } public void delete() { deleted = DELETED_STATUS; - } - public User getWriter() { - return question.getAuction().getSeller(); + if (question != EMPTY_QUESTION) { + question.deleteAnswer(); + question = EMPTY_QUESTION; + } } } diff --git a/backend/ddang/src/main/java/com/ddang/ddang/qna/domain/Question.java b/backend/ddang/src/main/java/com/ddang/ddang/qna/domain/Question.java index 6043ff435..36ceb9a88 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/qna/domain/Question.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/qna/domain/Question.java @@ -27,6 +27,7 @@ public class Question extends BaseCreateTimeEntity { private static final boolean DELETED_STATUS = true; + private static final Answer EMPTY_ANSWER = null; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @@ -71,4 +72,8 @@ public boolean isWriter(final User user) { public void delete() { deleted = DELETED_STATUS; } + + public void deleteAnswer() { + answer = EMPTY_ANSWER; + } } diff --git a/backend/ddang/src/main/java/com/ddang/ddang/qna/domain/repository/AnswerRepository.java b/backend/ddang/src/main/java/com/ddang/ddang/qna/domain/repository/AnswerRepository.java new file mode 100644 index 000000000..67adf0c12 --- /dev/null +++ b/backend/ddang/src/main/java/com/ddang/ddang/qna/domain/repository/AnswerRepository.java @@ -0,0 +1,14 @@ +package com.ddang.ddang.qna.domain.repository; + +import com.ddang.ddang.qna.domain.Answer; + +import java.util.Optional; + +public interface AnswerRepository { + + Answer save(final Answer answer); + + boolean existsByQuestionId(final Long questionId); + + Optional findById(final Long id); +} diff --git a/backend/ddang/src/main/java/com/ddang/ddang/qna/domain/repository/QuestionRepository.java b/backend/ddang/src/main/java/com/ddang/ddang/qna/domain/repository/QuestionRepository.java new file mode 100644 index 000000000..6da4f42bd --- /dev/null +++ b/backend/ddang/src/main/java/com/ddang/ddang/qna/domain/repository/QuestionRepository.java @@ -0,0 +1,15 @@ +package com.ddang.ddang.qna.domain.repository; + +import com.ddang.ddang.qna.domain.Question; + +import java.util.List; +import java.util.Optional; + +public interface QuestionRepository { + + Question save(final Question question); + + Optional findById(final Long id); + + List findAllByAuctionId(final Long auctionId); +} diff --git a/backend/ddang/src/main/java/com/ddang/ddang/qna/infrastructure/AnswerRepositoryImpl.java b/backend/ddang/src/main/java/com/ddang/ddang/qna/infrastructure/AnswerRepositoryImpl.java new file mode 100644 index 000000000..ed8069bfd --- /dev/null +++ b/backend/ddang/src/main/java/com/ddang/ddang/qna/infrastructure/AnswerRepositoryImpl.java @@ -0,0 +1,30 @@ +package com.ddang.ddang.qna.infrastructure; + +import com.ddang.ddang.qna.domain.Answer; +import com.ddang.ddang.qna.domain.repository.AnswerRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Repository; + +import java.util.Optional; + +@Repository +@RequiredArgsConstructor +public class AnswerRepositoryImpl implements AnswerRepository { + + private final JpaAnswerRepository jpaAnswerRepository; + + @Override + public Answer save(final Answer answer) { + return jpaAnswerRepository.save(answer); + } + + @Override + public boolean existsByQuestionId(final Long questionId) { + return jpaAnswerRepository.existsByQuestionId(questionId); + } + + @Override + public Optional findById(final Long id) { + return jpaAnswerRepository.findByIdAndDeletedIsFalse(id); + } +} diff --git a/backend/ddang/src/main/java/com/ddang/ddang/qna/infrastructure/JpaAnswerRepository.java b/backend/ddang/src/main/java/com/ddang/ddang/qna/infrastructure/JpaAnswerRepository.java index eea851954..ffd0167ca 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/qna/infrastructure/JpaAnswerRepository.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/qna/infrastructure/JpaAnswerRepository.java @@ -1,8 +1,8 @@ package com.ddang.ddang.qna.infrastructure; import com.ddang.ddang.qna.domain.Answer; -import org.springframework.data.jpa.repository.EntityGraph; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; import java.util.Optional; @@ -10,6 +10,13 @@ public interface JpaAnswerRepository extends JpaRepository { boolean existsByQuestionId(Long questionId); - @EntityGraph(attributePaths = {"question", "question.auction", "question.auction.seller"}) + @Query(""" + SELECT an + FROM Answer an + JOIN FETCH an.question q + JOIN FETCH q.auction a + JOIN FETCH a.seller + WHERE an.deleted = false AND an.id = :answerId + """) Optional findByIdAndDeletedIsFalse(final Long answerId); } diff --git a/backend/ddang/src/main/java/com/ddang/ddang/qna/infrastructure/JpaQuestionRepository.java b/backend/ddang/src/main/java/com/ddang/ddang/qna/infrastructure/JpaQuestionRepository.java index 615e04d2b..11de78514 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/qna/infrastructure/JpaQuestionRepository.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/qna/infrastructure/JpaQuestionRepository.java @@ -1,8 +1,8 @@ package com.ddang.ddang.qna.infrastructure; import com.ddang.ddang.qna.domain.Question; -import org.springframework.data.jpa.repository.EntityGraph; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; import java.util.List; import java.util.Optional; @@ -11,6 +11,14 @@ public interface JpaQuestionRepository extends JpaRepository { Optional findByIdAndDeletedIsFalse(final Long id); - @EntityGraph(attributePaths = {"writer", "answer", "auction", "auction.seller"}) + @Query(""" + SELECT q + FROM Question q + JOIN FETCH q.writer + LEFT JOIN FETCH q.answer + JOIN FETCH q.auction a + JOIN FETCH a.seller + WHERE q.deleted = false AND a.id = :auctionId + """) List findAllByAuctionId(final Long auctionId); } diff --git a/backend/ddang/src/main/java/com/ddang/ddang/qna/infrastructure/QuestionRepositoryImpl.java b/backend/ddang/src/main/java/com/ddang/ddang/qna/infrastructure/QuestionRepositoryImpl.java new file mode 100644 index 000000000..fd107d99a --- /dev/null +++ b/backend/ddang/src/main/java/com/ddang/ddang/qna/infrastructure/QuestionRepositoryImpl.java @@ -0,0 +1,31 @@ +package com.ddang.ddang.qna.infrastructure; + +import com.ddang.ddang.qna.domain.Question; +import com.ddang.ddang.qna.domain.repository.QuestionRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.Optional; + +@Repository +@RequiredArgsConstructor +public class QuestionRepositoryImpl implements QuestionRepository { + + private final JpaQuestionRepository jpaQuestionRepository; + + @Override + public Question save(final Question question) { + return jpaQuestionRepository.save(question); + } + + @Override + public Optional findById(final Long id) { + return jpaQuestionRepository.findByIdAndDeletedIsFalse(id); + } + + @Override + public List findAllByAuctionId(final Long auctionId) { + return jpaQuestionRepository.findAllByAuctionId(auctionId); + } +} diff --git a/backend/ddang/src/main/java/com/ddang/ddang/qna/presentation/QnaController.java b/backend/ddang/src/main/java/com/ddang/ddang/qna/presentation/QnaController.java index 184017777..0c56c7f5e 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/qna/presentation/QnaController.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/qna/presentation/QnaController.java @@ -2,12 +2,13 @@ import com.ddang.ddang.authentication.configuration.AuthenticateUser; import com.ddang.ddang.authentication.domain.dto.AuthenticationUserInfo; +import com.ddang.ddang.image.presentation.util.ImageRelativeUrl; import com.ddang.ddang.qna.application.AnswerService; import com.ddang.ddang.qna.application.QuestionService; -import com.ddang.ddang.qna.presentation.dto.request.CreateAnswerRequest; -import com.ddang.ddang.qna.presentation.dto.request.CreateQuestionRequest; import com.ddang.ddang.qna.application.dto.CreateAnswerDto; import com.ddang.ddang.qna.application.dto.CreateQuestionDto; +import com.ddang.ddang.qna.presentation.dto.request.CreateAnswerRequest; +import com.ddang.ddang.qna.presentation.dto.request.CreateQuestionRequest; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; @@ -33,32 +34,38 @@ public ResponseEntity createQuestion( @AuthenticateUser AuthenticationUserInfo userInfo, @RequestBody @Valid final CreateQuestionRequest questionRequest ) { - questionService.create(CreateQuestionDto.of(questionRequest, userInfo.userId())); + questionService.create( + CreateQuestionDto.of(questionRequest, userInfo.userId()), + ImageRelativeUrl.AUCTION.calculateAbsoluteUrl() + ); return ResponseEntity.created(URI.create("/auctions/" + questionRequest.auctionId())) .build(); } - @PostMapping("/{questionId}/answers") - public ResponseEntity createAnswer( + @DeleteMapping("/{questionId}") + public ResponseEntity deleteQuestion( @AuthenticateUser AuthenticationUserInfo userInfo, - @PathVariable final Long questionId, - @RequestBody @Valid final CreateAnswerRequest answerRequest + @PathVariable final Long questionId ) { - answerService.create(CreateAnswerDto.of(questionId, answerRequest, userInfo.userId())); + questionService.deleteById(questionId, userInfo.userId()); - return ResponseEntity.created(URI.create("/auctions/" + answerRequest.auctionId())) + return ResponseEntity.noContent() .build(); } - @DeleteMapping("/{questionId}") - public ResponseEntity deleteQuestion( + @PostMapping("/{questionId}/answers") + public ResponseEntity createAnswer( @AuthenticateUser AuthenticationUserInfo userInfo, - @PathVariable final Long questionId + @PathVariable final Long questionId, + @RequestBody @Valid final CreateAnswerRequest answerRequest ) { - questionService.deleteById(questionId, userInfo.userId()); + answerService.create( + CreateAnswerDto.of(questionId, answerRequest, userInfo.userId()), + ImageRelativeUrl.AUCTION.calculateAbsoluteUrl() + ); - return ResponseEntity.noContent() + return ResponseEntity.created(URI.create("/auctions/" + answerRequest.auctionId())) .build(); } diff --git a/backend/ddang/src/main/java/com/ddang/ddang/region/application/RegionService.java b/backend/ddang/src/main/java/com/ddang/ddang/region/application/RegionService.java index 9b6583f27..9df0a51ad 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/region/application/RegionService.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/region/application/RegionService.java @@ -4,7 +4,7 @@ import com.ddang.ddang.region.application.exception.RegionNotFoundException; import com.ddang.ddang.region.domain.InitializationRegionProcessor; import com.ddang.ddang.region.domain.Region; -import com.ddang.ddang.region.infrastructure.persistence.JpaRegionRepository; +import com.ddang.ddang.region.domain.repository.RegionRepository; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -16,7 +16,7 @@ @RequiredArgsConstructor public class RegionService { - private final JpaRegionRepository regionRepository; + private final RegionRepository regionRepository; private final InitializationRegionProcessor regionProcessor; @Transactional diff --git a/backend/ddang/src/main/java/com/ddang/ddang/region/domain/repository/RegionRepository.java b/backend/ddang/src/main/java/com/ddang/ddang/region/domain/repository/RegionRepository.java new file mode 100644 index 000000000..0f2f039ea --- /dev/null +++ b/backend/ddang/src/main/java/com/ddang/ddang/region/domain/repository/RegionRepository.java @@ -0,0 +1,23 @@ +package com.ddang.ddang.region.domain.repository; + +import com.ddang.ddang.region.domain.Region; + +import java.util.List; +import java.util.Optional; + +public interface RegionRepository { + + Region save(final Region region); + + List saveAll(final List regions); + + List findFirstAll(); + + List findSecondAllByFirstRegionId(final Long firstRegionId); + + List findThirdAllByFirstAndSecondRegionId(final Long firstRegionId, final Long secondRegionId); + + Optional findThirdRegionById(final Long thirdRegionId); + + List findAllThirdRegionByIds(final List thirdRegionIds); +} diff --git a/backend/ddang/src/main/java/com/ddang/ddang/region/infrastructure/persistence/RegionRepositoryImpl.java b/backend/ddang/src/main/java/com/ddang/ddang/region/infrastructure/persistence/RegionRepositoryImpl.java new file mode 100644 index 000000000..0847afd30 --- /dev/null +++ b/backend/ddang/src/main/java/com/ddang/ddang/region/infrastructure/persistence/RegionRepositoryImpl.java @@ -0,0 +1,51 @@ +package com.ddang.ddang.region.infrastructure.persistence; + +import com.ddang.ddang.region.domain.Region; +import com.ddang.ddang.region.domain.repository.RegionRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.Optional; + +@Repository +@RequiredArgsConstructor +public class RegionRepositoryImpl implements RegionRepository { + + private final JpaRegionRepository jpaRegionRepository; + + @Override + public Region save(final Region region) { + return jpaRegionRepository.save(region); + } + + @Override + public List saveAll(final List regions) { + return jpaRegionRepository.saveAll(regions); + } + + @Override + public List findFirstAll() { + return jpaRegionRepository.findFirstAll(); + } + + @Override + public List findSecondAllByFirstRegionId(final Long firstRegionId) { + return jpaRegionRepository.findSecondAllByFirstRegionId(firstRegionId); + } + + @Override + public List findThirdAllByFirstAndSecondRegionId(final Long firstRegionId, final Long secondRegionId) { + return jpaRegionRepository.findThirdAllByFirstAndSecondRegionId(firstRegionId, secondRegionId); + } + + @Override + public Optional findThirdRegionById(final Long thirdRegionId) { + return jpaRegionRepository.findThirdRegionById(thirdRegionId); + } + + @Override + public List findAllThirdRegionByIds(final List thirdRegionIds) { + return jpaRegionRepository.findAllThirdRegionByIds(thirdRegionIds); + } +} diff --git a/backend/ddang/src/main/java/com/ddang/ddang/report/application/AnswerReportService.java b/backend/ddang/src/main/java/com/ddang/ddang/report/application/AnswerReportService.java index ff402f96b..ed67037e7 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/report/application/AnswerReportService.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/report/application/AnswerReportService.java @@ -2,15 +2,15 @@ import com.ddang.ddang.qna.application.exception.AnswerNotFoundException; import com.ddang.ddang.qna.domain.Answer; -import com.ddang.ddang.qna.infrastructure.JpaAnswerRepository; +import com.ddang.ddang.qna.domain.repository.AnswerRepository; import com.ddang.ddang.report.application.dto.CreateAnswerReportDto; import com.ddang.ddang.report.application.dto.ReadAnswerReportDto; import com.ddang.ddang.report.application.exception.InvalidAnswererReportException; import com.ddang.ddang.report.domain.AnswerReport; -import com.ddang.ddang.report.infrastructure.persistence.JpaAnswerReportRepository; +import com.ddang.ddang.report.domain.repository.AnswerReportRepository; import com.ddang.ddang.user.application.exception.UserNotFoundException; import com.ddang.ddang.user.domain.User; -import com.ddang.ddang.user.infrastructure.persistence.JpaUserRepository; +import com.ddang.ddang.user.domain.repository.UserRepository; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -22,17 +22,17 @@ @RequiredArgsConstructor public class AnswerReportService { - private final JpaAnswerRepository answerRepository; - private final JpaUserRepository userRepository; - private final JpaAnswerReportRepository answerReportRepository; + private final AnswerRepository answerRepository; + private final UserRepository userRepository; + private final AnswerReportRepository answerReportRepository; @Transactional public Long create(final CreateAnswerReportDto answerReportDto) { - final Answer answer = answerRepository.findByIdAndDeletedIsFalse(answerReportDto.answerId()) + final Answer answer = answerRepository.findById(answerReportDto.answerId()) .orElseThrow(() -> new AnswerNotFoundException("해당 답변을 찾을 수 없습니다.") ); - final User reporter = userRepository.findByIdAndDeletedIsFalse(answerReportDto.reporterId()) + final User reporter = userRepository.findById(answerReportDto.reporterId()) .orElseThrow(() -> new UserNotFoundException("해당 사용자를 찾을 수 없습니다.")); checkInvalidAnswerReport(reporter, answer); @@ -52,7 +52,7 @@ private void checkInvalidAnswerReport(final User reporter, final Answer answer) } public List readAll() { - final List answerReports = answerReportRepository.findAllByOrderByIdAsc(); + final List answerReports = answerReportRepository.findAll(); return answerReports.stream() .map(ReadAnswerReportDto::from) diff --git a/backend/ddang/src/main/java/com/ddang/ddang/report/application/AuctionReportService.java b/backend/ddang/src/main/java/com/ddang/ddang/report/application/AuctionReportService.java index d38c222f1..5660f96f6 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/report/application/AuctionReportService.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/report/application/AuctionReportService.java @@ -2,17 +2,16 @@ import com.ddang.ddang.auction.application.exception.AuctionNotFoundException; import com.ddang.ddang.auction.domain.Auction; -import com.ddang.ddang.auction.infrastructure.persistence.JpaAuctionRepository; +import com.ddang.ddang.auction.domain.repository.AuctionRepository; import com.ddang.ddang.report.application.dto.CreateAuctionReportDto; import com.ddang.ddang.report.application.dto.ReadAuctionReportDto; import com.ddang.ddang.report.application.exception.AlreadyReportAuctionException; -import com.ddang.ddang.report.application.exception.InvalidReportAuctionException; import com.ddang.ddang.report.application.exception.InvalidReporterToAuctionException; import com.ddang.ddang.report.domain.AuctionReport; -import com.ddang.ddang.report.infrastructure.persistence.JpaAuctionReportRepository; +import com.ddang.ddang.report.domain.repository.AuctionReportRepository; import com.ddang.ddang.user.application.exception.UserNotFoundException; import com.ddang.ddang.user.domain.User; -import com.ddang.ddang.user.infrastructure.persistence.JpaUserRepository; +import com.ddang.ddang.user.domain.repository.UserRepository; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -24,15 +23,15 @@ @RequiredArgsConstructor public class AuctionReportService { - private final JpaAuctionRepository auctionRepository; - private final JpaUserRepository userRepository; - private final JpaAuctionReportRepository auctionReportRepository; + private final AuctionRepository auctionRepository; + private final UserRepository userRepository; + private final AuctionReportRepository auctionReportRepository; @Transactional public Long create(final CreateAuctionReportDto auctionReportDto) { final User reporter = userRepository.findById(auctionReportDto.reporterId()) .orElseThrow(() -> new UserNotFoundException("해당 사용자를 찾을 수 없습니다.")); - final Auction auction = auctionRepository.findById(auctionReportDto.auctionId()) + final Auction auction = auctionRepository.findTotalAuctionById(auctionReportDto.auctionId()) .orElseThrow(() -> new AuctionNotFoundException("해당 경매를 찾을 수 없습니다.")); checkInvalidAuctionReport(reporter, auction); @@ -46,16 +45,13 @@ private void checkInvalidAuctionReport(final User reporter, final Auction auctio if (auction.isOwner(reporter)) { throw new InvalidReporterToAuctionException("본인 경매글입니다."); } - if (auction.isDeleted()) { - throw new InvalidReportAuctionException("이미 삭제된 경매입니다."); - } if (auctionReportRepository.existsByAuctionIdAndReporterId(auction.getId(), reporter.getId())) { throw new AlreadyReportAuctionException("이미 신고한 경매입니다."); } } public List readAll() { - final List auctionReports = auctionReportRepository.findAllByOrderByIdAsc(); + final List auctionReports = auctionReportRepository.findAll(); return auctionReports.stream() .map(ReadAuctionReportDto::from) diff --git a/backend/ddang/src/main/java/com/ddang/ddang/report/application/ChatRoomReportService.java b/backend/ddang/src/main/java/com/ddang/ddang/report/application/ChatRoomReportService.java index 7a3613e79..186f6ce5f 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/report/application/ChatRoomReportService.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/report/application/ChatRoomReportService.java @@ -2,16 +2,16 @@ import com.ddang.ddang.chat.application.exception.ChatRoomNotFoundException; import com.ddang.ddang.chat.domain.ChatRoom; -import com.ddang.ddang.chat.infrastructure.persistence.JpaChatRoomRepository; +import com.ddang.ddang.chat.domain.repository.ChatRoomRepository; import com.ddang.ddang.report.application.dto.CreateChatRoomReportDto; import com.ddang.ddang.report.application.dto.ReadChatRoomReportDto; import com.ddang.ddang.report.application.exception.AlreadyReportChatRoomException; import com.ddang.ddang.report.application.exception.InvalidChatRoomReportException; import com.ddang.ddang.report.domain.ChatRoomReport; -import com.ddang.ddang.report.infrastructure.persistence.JpaChatRoomReportRepository; +import com.ddang.ddang.report.domain.repository.ChatRoomReportRepository; import com.ddang.ddang.user.application.exception.UserNotFoundException; import com.ddang.ddang.user.domain.User; -import com.ddang.ddang.user.infrastructure.persistence.JpaUserRepository; +import com.ddang.ddang.user.domain.repository.UserRepository; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -23,9 +23,9 @@ @RequiredArgsConstructor public class ChatRoomReportService { - private final JpaUserRepository userRepository; - private final JpaChatRoomRepository chatRoomRepository; - private final JpaChatRoomReportRepository chatRoomReportRepository; + private final UserRepository userRepository; + private final ChatRoomRepository chatRoomRepository; + private final ChatRoomReportRepository chatRoomReportRepository; @Transactional public Long create(final CreateChatRoomReportDto chatRoomReportDto) { @@ -52,7 +52,7 @@ private void checkInvalidChatRoomReport(final User reporter, final ChatRoom chat } public List readAll() { - final List auctionReports = chatRoomReportRepository.findAllByOrderByIdAsc(); + final List auctionReports = chatRoomReportRepository.findAll(); return auctionReports.stream() .map(ReadChatRoomReportDto::from) diff --git a/backend/ddang/src/main/java/com/ddang/ddang/report/application/QuestionReportService.java b/backend/ddang/src/main/java/com/ddang/ddang/report/application/QuestionReportService.java index c99192aff..f47825c45 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/report/application/QuestionReportService.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/report/application/QuestionReportService.java @@ -2,15 +2,15 @@ import com.ddang.ddang.qna.application.exception.QuestionNotFoundException; import com.ddang.ddang.qna.domain.Question; -import com.ddang.ddang.qna.infrastructure.JpaQuestionRepository; +import com.ddang.ddang.qna.domain.repository.QuestionRepository; import com.ddang.ddang.report.application.dto.CreateQuestionReportDto; import com.ddang.ddang.report.application.dto.ReadQuestionReportDto; import com.ddang.ddang.report.application.exception.InvalidQuestionReportException; import com.ddang.ddang.report.domain.QuestionReport; -import com.ddang.ddang.report.infrastructure.persistence.JpaQuestionReportRepository; +import com.ddang.ddang.report.domain.repository.QuestionReportRepository; import com.ddang.ddang.user.application.exception.UserNotFoundException; import com.ddang.ddang.user.domain.User; -import com.ddang.ddang.user.infrastructure.persistence.JpaUserRepository; +import com.ddang.ddang.user.domain.repository.UserRepository; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -22,17 +22,17 @@ @RequiredArgsConstructor public class QuestionReportService { - private final JpaQuestionRepository questionRepository; - private final JpaUserRepository userRepository; - private final JpaQuestionReportRepository questionReportRepository; + private final QuestionRepository questionRepository; + private final UserRepository userRepository; + private final QuestionReportRepository questionReportRepository; @Transactional public Long create(final CreateQuestionReportDto questionReportDto) { - final Question question = questionRepository.findByIdAndDeletedIsFalse(questionReportDto.questionId()) + final Question question = questionRepository.findById(questionReportDto.questionId()) .orElseThrow(() -> new QuestionNotFoundException("해당 질문을 찾을 수 없습니다.") ); - final User reporter = userRepository.findByIdAndDeletedIsFalse(questionReportDto.reporterId()) + final User reporter = userRepository.findById(questionReportDto.reporterId()) .orElseThrow(() -> new UserNotFoundException("해당 사용자를 찾을 수 없습니다.")); checkInvalidQuestionReport(reporter, question); @@ -52,10 +52,10 @@ private void checkInvalidQuestionReport(final User reporter, final Question ques } public List readAll() { - final List questionReports = questionReportRepository.findAllByOrderByIdAsc(); + final List questionReports = questionReportRepository.findAll(); return questionReports.stream() - .map(ReadQuestionReportDto::from) - .toList(); + .map(ReadQuestionReportDto::from) + .toList(); } } diff --git a/backend/ddang/src/main/java/com/ddang/ddang/report/application/dto/ReadUserInReportDto.java b/backend/ddang/src/main/java/com/ddang/ddang/report/application/dto/ReadUserInReportDto.java index dbc8db994..cd4fbd220 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/report/application/dto/ReadUserInReportDto.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/report/application/dto/ReadUserInReportDto.java @@ -18,7 +18,7 @@ public static ReadUserInReportDto from(final User user) { user.getName(), ImageIdProcessor.process(user.getProfileImage()), user.getReliability().getValue(), - user.getOauthId(), + user.getOauthInformation().getOauthId(), user.isDeleted() ); } diff --git a/backend/ddang/src/main/java/com/ddang/ddang/report/domain/QuestionReport.java b/backend/ddang/src/main/java/com/ddang/ddang/report/domain/QuestionReport.java index 1feea6a4a..c0abc9ad2 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/report/domain/QuestionReport.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/report/domain/QuestionReport.java @@ -21,7 +21,7 @@ @Entity @NoArgsConstructor(access = AccessLevel.PROTECTED) @Getter -@EqualsAndHashCode(of = "id") +@EqualsAndHashCode(of = "id", callSuper = false) @ToString(of = {"id", "description"}) public class QuestionReport extends BaseCreateTimeEntity { diff --git a/backend/ddang/src/main/java/com/ddang/ddang/report/domain/repository/AnswerReportRepository.java b/backend/ddang/src/main/java/com/ddang/ddang/report/domain/repository/AnswerReportRepository.java new file mode 100644 index 000000000..be7c7a79a --- /dev/null +++ b/backend/ddang/src/main/java/com/ddang/ddang/report/domain/repository/AnswerReportRepository.java @@ -0,0 +1,14 @@ +package com.ddang.ddang.report.domain.repository; + +import com.ddang.ddang.report.domain.AnswerReport; + +import java.util.List; + +public interface AnswerReportRepository { + + AnswerReport save(final AnswerReport answerReport); + + boolean existsByAnswerIdAndReporterId(final Long answerId, final Long reportId); + + List findAll(); +} diff --git a/backend/ddang/src/main/java/com/ddang/ddang/report/domain/repository/AuctionReportRepository.java b/backend/ddang/src/main/java/com/ddang/ddang/report/domain/repository/AuctionReportRepository.java new file mode 100644 index 000000000..5584de605 --- /dev/null +++ b/backend/ddang/src/main/java/com/ddang/ddang/report/domain/repository/AuctionReportRepository.java @@ -0,0 +1,14 @@ +package com.ddang.ddang.report.domain.repository; + +import com.ddang.ddang.report.domain.AuctionReport; + +import java.util.List; + +public interface AuctionReportRepository { + + AuctionReport save(final AuctionReport auctionReport); + + boolean existsByAuctionIdAndReporterId(final Long auctionId, final Long reporterId); + + List findAll(); +} diff --git a/backend/ddang/src/main/java/com/ddang/ddang/report/domain/repository/ChatRoomReportRepository.java b/backend/ddang/src/main/java/com/ddang/ddang/report/domain/repository/ChatRoomReportRepository.java new file mode 100644 index 000000000..20f4425e9 --- /dev/null +++ b/backend/ddang/src/main/java/com/ddang/ddang/report/domain/repository/ChatRoomReportRepository.java @@ -0,0 +1,14 @@ +package com.ddang.ddang.report.domain.repository; + +import com.ddang.ddang.report.domain.ChatRoomReport; + +import java.util.List; + +public interface ChatRoomReportRepository { + + ChatRoomReport save(final ChatRoomReport chatRoomReport); + + boolean existsByChatRoomIdAndReporterId(final Long chatRoomId, final Long reporterId); + + List findAll(); +} diff --git a/backend/ddang/src/main/java/com/ddang/ddang/report/domain/repository/QuestionReportRepository.java b/backend/ddang/src/main/java/com/ddang/ddang/report/domain/repository/QuestionReportRepository.java new file mode 100644 index 000000000..d13618d6e --- /dev/null +++ b/backend/ddang/src/main/java/com/ddang/ddang/report/domain/repository/QuestionReportRepository.java @@ -0,0 +1,14 @@ +package com.ddang.ddang.report.domain.repository; + +import com.ddang.ddang.report.domain.QuestionReport; + +import java.util.List; + +public interface QuestionReportRepository { + + QuestionReport save(final QuestionReport questionReport); + + boolean existsByQuestionIdAndReporterId(final Long questionId, final Long reporterId); + + List findAll(); +} diff --git a/backend/ddang/src/main/java/com/ddang/ddang/report/infrastructure/persistence/AnswerReportRepositoryImpl.java b/backend/ddang/src/main/java/com/ddang/ddang/report/infrastructure/persistence/AnswerReportRepositoryImpl.java new file mode 100644 index 000000000..c65ef0d32 --- /dev/null +++ b/backend/ddang/src/main/java/com/ddang/ddang/report/infrastructure/persistence/AnswerReportRepositoryImpl.java @@ -0,0 +1,30 @@ +package com.ddang.ddang.report.infrastructure.persistence; + +import com.ddang.ddang.report.domain.AnswerReport; +import com.ddang.ddang.report.domain.repository.AnswerReportRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +@RequiredArgsConstructor +public class AnswerReportRepositoryImpl implements AnswerReportRepository { + + private final JpaAnswerReportRepository jpaAnswerReportRepository; + + @Override + public AnswerReport save(final AnswerReport answerReport) { + return jpaAnswerReportRepository.save(answerReport); + } + + @Override + public boolean existsByAnswerIdAndReporterId(final Long answerId, final Long reportId) { + return jpaAnswerReportRepository.existsByAnswerIdAndReporterId(answerId, reportId); + } + + @Override + public List findAll() { + return jpaAnswerReportRepository.findAll(); + } +} diff --git a/backend/ddang/src/main/java/com/ddang/ddang/report/infrastructure/persistence/AuctionReportRepositoryImpl.java b/backend/ddang/src/main/java/com/ddang/ddang/report/infrastructure/persistence/AuctionReportRepositoryImpl.java new file mode 100644 index 000000000..994363702 --- /dev/null +++ b/backend/ddang/src/main/java/com/ddang/ddang/report/infrastructure/persistence/AuctionReportRepositoryImpl.java @@ -0,0 +1,30 @@ +package com.ddang.ddang.report.infrastructure.persistence; + +import com.ddang.ddang.report.domain.AuctionReport; +import com.ddang.ddang.report.domain.repository.AuctionReportRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +@RequiredArgsConstructor +public class AuctionReportRepositoryImpl implements AuctionReportRepository { + + private final JpaAuctionReportRepository jpaAuctionReportRepository; + + @Override + public AuctionReport save(final AuctionReport auctionReport) { + return jpaAuctionReportRepository.save(auctionReport); + } + + @Override + public boolean existsByAuctionIdAndReporterId(final Long auctionId, final Long reporterId) { + return jpaAuctionReportRepository.existsByAuctionIdAndReporterId(auctionId, reporterId); + } + + @Override + public List findAll() { + return jpaAuctionReportRepository.findAll(); + } +} diff --git a/backend/ddang/src/main/java/com/ddang/ddang/report/infrastructure/persistence/ChatRoomReportRepositoryImpl.java b/backend/ddang/src/main/java/com/ddang/ddang/report/infrastructure/persistence/ChatRoomReportRepositoryImpl.java new file mode 100644 index 000000000..8212f3249 --- /dev/null +++ b/backend/ddang/src/main/java/com/ddang/ddang/report/infrastructure/persistence/ChatRoomReportRepositoryImpl.java @@ -0,0 +1,30 @@ +package com.ddang.ddang.report.infrastructure.persistence; + +import com.ddang.ddang.report.domain.ChatRoomReport; +import com.ddang.ddang.report.domain.repository.ChatRoomReportRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +@RequiredArgsConstructor +public class ChatRoomReportRepositoryImpl implements ChatRoomReportRepository { + + private final JpaChatRoomReportRepository jpaChatRoomReportRepository; + + @Override + public ChatRoomReport save(final ChatRoomReport chatRoomReport) { + return jpaChatRoomReportRepository.save(chatRoomReport); + } + + @Override + public boolean existsByChatRoomIdAndReporterId(final Long chatRoomId, final Long reporterId) { + return jpaChatRoomReportRepository.existsByChatRoomIdAndReporterId(chatRoomId, reporterId); + } + + @Override + public List findAll() { + return jpaChatRoomReportRepository.findAll(); + } +} diff --git a/backend/ddang/src/main/java/com/ddang/ddang/report/infrastructure/persistence/JpaAnswerReportRepository.java b/backend/ddang/src/main/java/com/ddang/ddang/report/infrastructure/persistence/JpaAnswerReportRepository.java index 46dcac184..2be18d491 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/report/infrastructure/persistence/JpaAnswerReportRepository.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/report/infrastructure/persistence/JpaAnswerReportRepository.java @@ -1,15 +1,24 @@ package com.ddang.ddang.report.infrastructure.persistence; import com.ddang.ddang.report.domain.AnswerReport; -import org.springframework.data.jpa.repository.EntityGraph; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; import java.util.List; public interface JpaAnswerReportRepository extends JpaRepository { - boolean existsByAnswerIdAndReporterId(final Long answerId, final Long ReporterId); + boolean existsByAnswerIdAndReporterId(final Long answerId, final Long reportId); - @EntityGraph(attributePaths = {"reporter", "answer", "answer.question", "answer.question.auction", "answer.question.auction.seller"}) - List findAllByOrderByIdAsc(); + @Query(""" + SELECT ar + FROM AnswerReport ar + JOIN FETCH ar.reporter + JOIN FETCH ar.answer an + JOIN FETCH an.question q + JOIN FETCH q.auction a + JOIN FETCH a.seller + ORDER BY ar.id ASC + """) + List findAll(); } diff --git a/backend/ddang/src/main/java/com/ddang/ddang/report/infrastructure/persistence/JpaAuctionReportRepository.java b/backend/ddang/src/main/java/com/ddang/ddang/report/infrastructure/persistence/JpaAuctionReportRepository.java index c426a4051..5fa4e4186 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/report/infrastructure/persistence/JpaAuctionReportRepository.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/report/infrastructure/persistence/JpaAuctionReportRepository.java @@ -1,8 +1,8 @@ package com.ddang.ddang.report.infrastructure.persistence; import com.ddang.ddang.report.domain.AuctionReport; -import org.springframework.data.jpa.repository.EntityGraph; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; import java.util.List; @@ -10,6 +10,13 @@ public interface JpaAuctionReportRepository extends JpaRepository findAllByOrderByIdAsc(); + @Query(""" + SELECT ar + FROM AuctionReport ar + JOIN FETCH ar.reporter + JOIN FETCH ar.auction a + JOIN FETCH a.seller + ORDER BY ar.id ASC + """) + List findAll(); } diff --git a/backend/ddang/src/main/java/com/ddang/ddang/report/infrastructure/persistence/JpaChatRoomReportRepository.java b/backend/ddang/src/main/java/com/ddang/ddang/report/infrastructure/persistence/JpaChatRoomReportRepository.java index d6965e7df..c41258456 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/report/infrastructure/persistence/JpaChatRoomReportRepository.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/report/infrastructure/persistence/JpaChatRoomReportRepository.java @@ -1,8 +1,8 @@ package com.ddang.ddang.report.infrastructure.persistence; import com.ddang.ddang.report.domain.ChatRoomReport; -import org.springframework.data.jpa.repository.EntityGraph; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; import java.util.List; @@ -10,6 +10,15 @@ public interface JpaChatRoomReportRepository extends JpaRepository findAllByOrderByIdAsc(); + @Query(""" + SELECT crr + FROM ChatRoomReport crr + JOIN FETCH crr.reporter + JOIN FETCH crr.chatRoom cr + JOIN FETCH cr.buyer + JOIN FETCH cr.auction a + JOIN FETCH a.seller + ORDER BY crr.id ASC + """) + List findAll(); } diff --git a/backend/ddang/src/main/java/com/ddang/ddang/report/infrastructure/persistence/JpaQuestionReportRepository.java b/backend/ddang/src/main/java/com/ddang/ddang/report/infrastructure/persistence/JpaQuestionReportRepository.java index 6f5e7006a..deaf6e349 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/report/infrastructure/persistence/JpaQuestionReportRepository.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/report/infrastructure/persistence/JpaQuestionReportRepository.java @@ -1,8 +1,8 @@ package com.ddang.ddang.report.infrastructure.persistence; import com.ddang.ddang.report.domain.QuestionReport; -import org.springframework.data.jpa.repository.EntityGraph; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; import java.util.List; @@ -10,6 +10,12 @@ public interface JpaQuestionReportRepository extends JpaRepository findAllByOrderByIdAsc(); + @Query(""" + SELECT qr + FROM QuestionReport qr + JOIN FETCH qr.reporter + JOIN FETCH qr.question + ORDER BY qr.id ASC + """) + List findAll(); } diff --git a/backend/ddang/src/main/java/com/ddang/ddang/report/infrastructure/persistence/QuestionReportRepositoryImpl.java b/backend/ddang/src/main/java/com/ddang/ddang/report/infrastructure/persistence/QuestionReportRepositoryImpl.java new file mode 100644 index 000000000..d5a7b2802 --- /dev/null +++ b/backend/ddang/src/main/java/com/ddang/ddang/report/infrastructure/persistence/QuestionReportRepositoryImpl.java @@ -0,0 +1,30 @@ +package com.ddang.ddang.report.infrastructure.persistence; + +import com.ddang.ddang.report.domain.QuestionReport; +import com.ddang.ddang.report.domain.repository.QuestionReportRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +@RequiredArgsConstructor +public class QuestionReportRepositoryImpl implements QuestionReportRepository { + + private final JpaQuestionReportRepository jpaQuestionReportRepository; + + @Override + public QuestionReport save(final QuestionReport questionReport) { + return jpaQuestionReportRepository.save(questionReport); + } + + @Override + public boolean existsByQuestionIdAndReporterId(final Long questionId, final Long reporterId) { + return jpaQuestionReportRepository.existsByQuestionIdAndReporterId(questionId, reporterId); + } + + @Override + public List findAll() { + return jpaQuestionReportRepository.findAll(); + } +} diff --git a/backend/ddang/src/main/java/com/ddang/ddang/report/presentation/dto/response/ReadReporterResponse.java b/backend/ddang/src/main/java/com/ddang/ddang/report/presentation/dto/response/ReadReporterResponse.java index 7cc03e1a0..9a17baa3d 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/report/presentation/dto/response/ReadReporterResponse.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/report/presentation/dto/response/ReadReporterResponse.java @@ -7,6 +7,7 @@ public record ReadReporterResponse(Long id, String name) { public static ReadReporterResponse from(final ReadReporterDto reporterDto) { final String name = NameProcessor.process(reporterDto.isDeleted(), reporterDto.name()); + return new ReadReporterResponse(reporterDto.id(), name); } } diff --git a/backend/ddang/src/main/java/com/ddang/ddang/review/application/ReviewService.java b/backend/ddang/src/main/java/com/ddang/ddang/review/application/ReviewService.java index fad0e2a06..187c85b90 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/review/application/ReviewService.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/review/application/ReviewService.java @@ -2,7 +2,7 @@ import com.ddang.ddang.auction.application.exception.AuctionNotFoundException; import com.ddang.ddang.auction.domain.Auction; -import com.ddang.ddang.auction.infrastructure.persistence.JpaAuctionRepository; +import com.ddang.ddang.auction.domain.repository.AuctionRepository; import com.ddang.ddang.review.application.dto.CreateReviewDto; import com.ddang.ddang.review.application.dto.ReadReviewDetailDto; import com.ddang.ddang.review.application.dto.ReadReviewDto; @@ -10,10 +10,10 @@ import com.ddang.ddang.review.application.exception.InvalidUserToReview; import com.ddang.ddang.review.application.exception.ReviewNotFoundException; import com.ddang.ddang.review.domain.Review; -import com.ddang.ddang.review.infrastructure.persistence.JpaReviewRepository; +import com.ddang.ddang.review.domain.repository.ReviewRepository; import com.ddang.ddang.user.application.exception.UserNotFoundException; import com.ddang.ddang.user.domain.User; -import com.ddang.ddang.user.infrastructure.persistence.JpaUserRepository; +import com.ddang.ddang.user.domain.repository.UserRepository; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -26,13 +26,13 @@ @RequiredArgsConstructor public class ReviewService { - private final JpaReviewRepository reviewRepository; - private final JpaAuctionRepository auctionRepository; - private final JpaUserRepository userRepository; + private final ReviewRepository reviewRepository; + private final AuctionRepository auctionRepository; + private final UserRepository userRepository; @Transactional public Long create(final CreateReviewDto reviewDto) { - final Auction findAuction = auctionRepository.findById(reviewDto.auctionId()) + final Auction findAuction = auctionRepository.findTotalAuctionById(reviewDto.auctionId()) .orElseThrow(() -> new AuctionNotFoundException("해당 경매를 찾을 수 없습니다.") ); @@ -44,9 +44,9 @@ public Long create(final CreateReviewDto reviewDto) { validateWriterCanReview(findAuction, writer); final Review review = reviewDto.toEntity(findAuction, writer, target); - final Review persistReview = saveReviewAndUpdateReliability(review, target); - return persistReview.getId(); + return reviewRepository.save(review) + .getId(); } private void validateWriterCanReview(final Auction auction, final User writer) { @@ -63,18 +63,9 @@ private void validateAlreadyReviewed(final Auction auction, final User writer) { } } - private Review saveReviewAndUpdateReliability(final Review review, final User target) { - final Review persistReview = reviewRepository.save(review); - - final List targetReviews = reviewRepository.findAllByTargetId(target.getId()); - target.updateReliability(targetReviews); - - return persistReview; - } - public ReadReviewDetailDto readByReviewId(final Long reviewId) { final Review findReview = reviewRepository.findById(reviewId) - .orElseThrow(() -> new ReviewNotFoundException("해당 평가를 찾을 수 없습니다.")); + .orElseThrow(() -> new ReviewNotFoundException("해당 평가를 찾을 수 없습니다.")); return ReadReviewDetailDto.from(findReview); } diff --git a/backend/ddang/src/main/java/com/ddang/ddang/review/application/dto/ReadUserInReviewDto.java b/backend/ddang/src/main/java/com/ddang/ddang/review/application/dto/ReadUserInReviewDto.java index ecbf938e8..bb0fa0927 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/review/application/dto/ReadUserInReviewDto.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/review/application/dto/ReadUserInReviewDto.java @@ -10,7 +10,7 @@ public static ReadUserInReviewDto from(final User user) { user.getName(), user.getProfileImage().getId(), user.getReliability().getValue(), - user.getOauthId() + user.getOauthInformation().getOauthId() ); } } diff --git a/backend/ddang/src/main/java/com/ddang/ddang/review/domain/Reviews.java b/backend/ddang/src/main/java/com/ddang/ddang/review/domain/Reviews.java new file mode 100644 index 000000000..87d5ad746 --- /dev/null +++ b/backend/ddang/src/main/java/com/ddang/ddang/review/domain/Reviews.java @@ -0,0 +1,52 @@ +package com.ddang.ddang.review.domain; + +import com.ddang.ddang.user.domain.User; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.ToString; + +import java.util.List; +import java.util.Set; + +import static java.util.stream.Collectors.toSet; + +@RequiredArgsConstructor +@Getter +@ToString +public class Reviews { + + private final List reviews; + + public double addAllReviewScore() { + return reviews.stream() + .mapToDouble(review -> review.getScore().getValue()) + .sum(); + } + + public int size() { + return reviews.size(); + } + + public boolean isEmpty() { + return reviews.isEmpty(); + } + + public Set findReviewTargets() { + return reviews.stream() + .map(Review::getTarget) + .collect(toSet()); + } + + public List findReviewsByTarget(final User targetUser) { + return reviews.stream() + .filter(review -> review.getTarget().equals(targetUser)) + .toList(); + } + + public Long findLastReviewId() { + final int lastIndex = reviews.size() - 1; + + return reviews.get(lastIndex) + .getId(); + } +} diff --git a/backend/ddang/src/main/java/com/ddang/ddang/review/domain/repository/ReviewRepository.java b/backend/ddang/src/main/java/com/ddang/ddang/review/domain/repository/ReviewRepository.java new file mode 100644 index 000000000..fb726b725 --- /dev/null +++ b/backend/ddang/src/main/java/com/ddang/ddang/review/domain/repository/ReviewRepository.java @@ -0,0 +1,21 @@ +package com.ddang.ddang.review.domain.repository; + +import com.ddang.ddang.review.domain.Review; + +import java.util.List; +import java.util.Optional; + +public interface ReviewRepository { + + Review save(final Review review); + + Optional findById(final Long id); + + List findAllByTargetId(final Long targetId); + + Optional findByAuctionIdAndWriterId(final Long auctionId, final Long writerId); + + List findAllByIdGreaterThan(final Long id); + + boolean existsByAuctionIdAndWriterId(final Long auctionId, final Long writerId); +} diff --git a/backend/ddang/src/main/java/com/ddang/ddang/review/infrastructure/persistence/JpaReviewRepository.java b/backend/ddang/src/main/java/com/ddang/ddang/review/infrastructure/persistence/JpaReviewRepository.java index 0d6e71e6a..5462534d6 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/review/infrastructure/persistence/JpaReviewRepository.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/review/infrastructure/persistence/JpaReviewRepository.java @@ -12,11 +12,15 @@ public interface JpaReviewRepository extends JpaRepository { boolean existsByAuctionIdAndWriterId(final Long auctionId, final Long writerId); @Query(""" - SELECT r FROM Review r JOIN FETCH r.writer w JOIN FETCH r.target t + SELECT r FROM Review r + JOIN FETCH r.writer w + JOIN FETCH r.target t WHERE t.id = :targetId ORDER BY r.id DESC """) List findAllByTargetId(final Long targetId); Optional findByAuctionIdAndWriterId(final Long auctionId, final Long writerId); + + List findAllByIdGreaterThan(final Long id); } diff --git a/backend/ddang/src/main/java/com/ddang/ddang/review/infrastructure/persistence/ReviewRepositoryImpl.java b/backend/ddang/src/main/java/com/ddang/ddang/review/infrastructure/persistence/ReviewRepositoryImpl.java new file mode 100644 index 000000000..cf5581243 --- /dev/null +++ b/backend/ddang/src/main/java/com/ddang/ddang/review/infrastructure/persistence/ReviewRepositoryImpl.java @@ -0,0 +1,46 @@ +package com.ddang.ddang.review.infrastructure.persistence; + +import com.ddang.ddang.review.domain.Review; +import com.ddang.ddang.review.domain.repository.ReviewRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.Optional; + +@Repository +@RequiredArgsConstructor +public class ReviewRepositoryImpl implements ReviewRepository { + + private final JpaReviewRepository jpaReviewRepository; + + @Override + public Review save(final Review review) { + return jpaReviewRepository.save(review); + } + + @Override + public Optional findById(final Long id) { + return jpaReviewRepository.findById(id); + } + + @Override + public List findAllByTargetId(final Long targetId) { + return jpaReviewRepository.findAllByTargetId(targetId); + } + + @Override + public Optional findByAuctionIdAndWriterId(final Long auctionId, final Long writerId) { + return jpaReviewRepository.findByAuctionIdAndWriterId(auctionId, writerId); + } + + @Override + public List findAllByIdGreaterThan(final Long id) { + return jpaReviewRepository.findAllByIdGreaterThan(id); + } + + @Override + public boolean existsByAuctionIdAndWriterId(final Long auctionId, final Long writerId) { + return jpaReviewRepository.existsByAuctionIdAndWriterId(auctionId, writerId); + } +} diff --git a/backend/ddang/src/main/java/com/ddang/ddang/user/application/UserService.java b/backend/ddang/src/main/java/com/ddang/ddang/user/application/UserService.java index 5dc1ca80c..c0cc24705 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/user/application/UserService.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/user/application/UserService.java @@ -4,9 +4,10 @@ import com.ddang.ddang.image.domain.dto.StoreImageDto; import com.ddang.ddang.user.application.dto.ReadUserDto; import com.ddang.ddang.user.application.dto.UpdateUserDto; +import com.ddang.ddang.user.application.exception.AlreadyExistsNameException; import com.ddang.ddang.user.application.exception.UserNotFoundException; import com.ddang.ddang.user.domain.User; -import com.ddang.ddang.user.infrastructure.persistence.JpaUserRepository; +import com.ddang.ddang.user.domain.repository.UserRepository; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -16,7 +17,7 @@ @RequiredArgsConstructor public class UserService { - private final JpaUserRepository userRepository; + private final UserRepository userRepository; private final StoreImageProcessor imageProcessor; public ReadUserDto readById(final Long userId) { @@ -28,7 +29,7 @@ public ReadUserDto readById(final Long userId) { @Transactional public ReadUserDto updateById(final Long userId, final UpdateUserDto userDto) { - final User user = userRepository.findByIdAndDeletedIsFalse(userId) + final User user = userRepository.findById(userId) .orElseThrow(() -> new UserNotFoundException("사용자 정보를 사용할 수 없습니다.")); updateUserByRequest(userDto, user); @@ -42,7 +43,14 @@ private void updateUserByRequest(final UpdateUserDto userDto, final User user) { user.updateProfileImage(storeImageDto.toProfileImageEntity()); } if (userDto.name() != null) { + validateAvailableName(userDto.name()); user.updateName(userDto.name()); } } + + private void validateAvailableName(final String name) { + if (userRepository.existsByName(name)) { + throw new AlreadyExistsNameException("이미 존재하는 닉네임입니다."); + } + } } diff --git a/backend/ddang/src/main/java/com/ddang/ddang/user/application/dto/ReadUserDto.java b/backend/ddang/src/main/java/com/ddang/ddang/user/application/dto/ReadUserDto.java index 15ca854b6..015310258 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/user/application/dto/ReadUserDto.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/user/application/dto/ReadUserDto.java @@ -18,7 +18,7 @@ public static ReadUserDto from(final User user) { user.getName(), ImageIdProcessor.process(user.getProfileImage()), user.getReliability().getValue(), - user.getOauthId(), + user.getOauthInformation().getOauthId(), user.isDeleted() ); } diff --git a/backend/ddang/src/main/java/com/ddang/ddang/user/application/exception/AlreadyExistsNameException.java b/backend/ddang/src/main/java/com/ddang/ddang/user/application/exception/AlreadyExistsNameException.java new file mode 100644 index 000000000..a5e0a9332 --- /dev/null +++ b/backend/ddang/src/main/java/com/ddang/ddang/user/application/exception/AlreadyExistsNameException.java @@ -0,0 +1,8 @@ +package com.ddang.ddang.user.application.exception; + +public class AlreadyExistsNameException extends IllegalArgumentException { + + public AlreadyExistsNameException(final String message) { + super(message); + } +} diff --git a/backend/ddang/src/main/java/com/ddang/ddang/user/application/schedule/ReliabilityUpdateSchedulingService.java b/backend/ddang/src/main/java/com/ddang/ddang/user/application/schedule/ReliabilityUpdateSchedulingService.java new file mode 100644 index 000000000..9601e3615 --- /dev/null +++ b/backend/ddang/src/main/java/com/ddang/ddang/user/application/schedule/ReliabilityUpdateSchedulingService.java @@ -0,0 +1,65 @@ +package com.ddang.ddang.user.application.schedule; + +import com.ddang.ddang.review.domain.Review; +import com.ddang.ddang.review.domain.Reviews; +import com.ddang.ddang.review.domain.repository.ReviewRepository; +import com.ddang.ddang.user.domain.ReliabilityUpdateHistory; +import com.ddang.ddang.user.domain.User; +import com.ddang.ddang.user.domain.UserReliability; +import com.ddang.ddang.user.domain.repository.ReliabilityUpdateHistoryRepository; +import com.ddang.ddang.user.domain.repository.UserReliabilityRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.Set; + +@Service +@Transactional(readOnly = true) +@RequiredArgsConstructor +public class ReliabilityUpdateSchedulingService { + + private final ReliabilityUpdateHistoryRepository updateHistoryRepository; + private final ReviewRepository reviewRepository; + private final UserReliabilityRepository userReliabilityRepository; + + @Transactional + @Scheduled(cron = "0 0 4 ? * MON") + public void updateAllUserReliability() { + final ReliabilityUpdateHistory updateHistory = updateHistoryRepository.findFirstByOrderByIdDesc() + .orElse(new ReliabilityUpdateHistory()); + final Long lastAppliedReviewId = updateHistory.getLastAppliedReviewId(); + final List findNewReviews = reviewRepository.findAllByIdGreaterThan(lastAppliedReviewId); + final Reviews newReviews = new Reviews(findNewReviews); + + if (newReviews.isEmpty()) { + return; + } + + update(newReviews); + } + + private void update(final Reviews newReviews) { + final Set targetUsers = newReviews.findReviewTargets(); + for (final User targetUser : targetUsers) { + final UserReliability userReliability = userReliabilityRepository.findByUserId(targetUser.getId()) + .orElseGet(() -> createUserReliability(targetUser)); + + final List targetReviews = newReviews.findReviewsByTarget(targetUser); + + userReliability.updateReliability(new Reviews(targetReviews)); + } + + final ReliabilityUpdateHistory newHistory = new ReliabilityUpdateHistory(newReviews.findLastReviewId()); + + updateHistoryRepository.save(newHistory); + } + + private UserReliability createUserReliability(final User user) { + final UserReliability userReliability = new UserReliability(user); + + return userReliabilityRepository.save(userReliability); + } +} diff --git a/backend/ddang/src/main/java/com/ddang/ddang/user/domain/OauthInformation.java b/backend/ddang/src/main/java/com/ddang/ddang/user/domain/OauthInformation.java new file mode 100644 index 000000000..7e069b7fa --- /dev/null +++ b/backend/ddang/src/main/java/com/ddang/ddang/user/domain/OauthInformation.java @@ -0,0 +1,31 @@ +package com.ddang.ddang.user.domain; + +import com.ddang.ddang.authentication.infrastructure.oauth2.Oauth2Type; +import jakarta.persistence.Column; +import jakarta.persistence.Embeddable; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import lombok.AccessLevel; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.ToString; + +@Embeddable +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@Getter +@EqualsAndHashCode +@ToString +public class OauthInformation { + + private String oauthId; + + @Column(name = "oauth2_type") + @Enumerated(EnumType.STRING) + private Oauth2Type oauth2Type; + + public OauthInformation(final String oauthId, final Oauth2Type oauth2Type) { + this.oauthId = oauthId; + this.oauth2Type = oauth2Type; + } +} diff --git a/backend/ddang/src/main/java/com/ddang/ddang/user/domain/Reliability.java b/backend/ddang/src/main/java/com/ddang/ddang/user/domain/Reliability.java index cda3996cf..22fe3e67f 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/user/domain/Reliability.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/user/domain/Reliability.java @@ -1,6 +1,5 @@ package com.ddang.ddang.user.domain; -import com.ddang.ddang.review.domain.Review; import jakarta.persistence.Embeddable; import lombok.AccessLevel; import lombok.EqualsAndHashCode; @@ -8,8 +7,6 @@ import lombok.NoArgsConstructor; import lombok.ToString; -import java.util.List; - @Embeddable @NoArgsConstructor(access = AccessLevel.PROTECTED) @Getter @@ -17,24 +14,16 @@ @ToString public class Reliability { - public static final Reliability INITIAL_RELIABILITY = new Reliability(null); + private static final double INITIAL_RELIABILITY_VALUE = Double.MIN_VALUE; + public static final Reliability INITIAL_RELIABILITY = new Reliability(INITIAL_RELIABILITY_VALUE); - private Double value; + private double value; - public Reliability(final Double value) { + public Reliability(final double value) { this.value = value; } - public void updateReliability(final List reviews) { - if (reviews.isEmpty()) { - this.value = null; - - return; - } - - this.value = reviews.stream() - .mapToDouble(review -> review.getScore().getValue()) - .average() - .orElseGet(null); + public double calculateReviewScoreSum(final int appliedReviewCount) { + return value * appliedReviewCount; } } diff --git a/backend/ddang/src/main/java/com/ddang/ddang/user/domain/ReliabilityUpdateHistory.java b/backend/ddang/src/main/java/com/ddang/ddang/user/domain/ReliabilityUpdateHistory.java new file mode 100644 index 000000000..31d670c54 --- /dev/null +++ b/backend/ddang/src/main/java/com/ddang/ddang/user/domain/ReliabilityUpdateHistory.java @@ -0,0 +1,29 @@ +package com.ddang.ddang.user.domain; + +import com.ddang.ddang.common.entity.BaseCreateTimeEntity; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.ToString; + +@Entity +@NoArgsConstructor +@Getter +@EqualsAndHashCode(of = "id", callSuper = false) +@ToString(of = {"id", "lastAppliedReviewId"}) +public class ReliabilityUpdateHistory extends BaseCreateTimeEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private Long lastAppliedReviewId = 0L; + + public ReliabilityUpdateHistory(final Long lastAppliedReviewId) { + this.lastAppliedReviewId = lastAppliedReviewId; + } +} diff --git a/backend/ddang/src/main/java/com/ddang/ddang/user/domain/User.java b/backend/ddang/src/main/java/com/ddang/ddang/user/domain/User.java index b6bcf6278..75f9072a9 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/user/domain/User.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/user/domain/User.java @@ -1,8 +1,8 @@ package com.ddang.ddang.user.domain; +import com.ddang.ddang.authentication.infrastructure.oauth2.Oauth2Type; import com.ddang.ddang.common.entity.BaseTimeEntity; import com.ddang.ddang.image.domain.ProfileImage; -import com.ddang.ddang.review.domain.Review; import jakarta.persistence.AttributeOverride; import jakarta.persistence.CascadeType; import jakarta.persistence.Column; @@ -23,18 +23,18 @@ import lombok.NoArgsConstructor; import lombok.ToString; -import java.util.List; +import java.util.UUID; @Entity @NoArgsConstructor(access = AccessLevel.PROTECTED) @Getter @EqualsAndHashCode(of = "id", callSuper = false) -@ToString(of = {"id", "name", "reliability", "oauthId", "deleted"}) +@ToString(of = {"id", "name", "reliability", "deleted", "oauthInformation"}) @Table(name = "users") public class User extends BaseTimeEntity { + public static final User EMPTY_USER = null; private static final boolean DELETED_STATUS = true; - private static final String UNKOWN_NAME = "알 수 없음"; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @@ -44,14 +44,15 @@ public class User extends BaseTimeEntity { private String name; @OneToOne(fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST, CascadeType.REMOVE}) - @JoinColumn(name = "profile_image_id", foreignKey = @ForeignKey(name = "fk_user_profile_image"), nullable = false) + @JoinColumn(name = "profile_image_id", foreignKey = @ForeignKey(name = "fk_user_profile_image")) private ProfileImage profileImage; @Embedded @AttributeOverride(name = "value", column = @Column(name = "reliability")) private Reliability reliability; - private String oauthId; + @Embedded + private OauthInformation oauthInformation; @Column(name = "is_deleted") private boolean deleted = false; @@ -61,12 +62,13 @@ private User( final String name, final ProfileImage profileImage, final Reliability reliability, - final String oauthId + final String oauthId, + final Oauth2Type oauth2Type ) { this.name = name; this.profileImage = profileImage; this.reliability = processReliability(reliability); - this.oauthId = oauthId; + this.oauthInformation = new OauthInformation(oauthId, oauth2Type); } private Reliability processReliability(final Reliability reliability) { @@ -87,9 +89,11 @@ public void updateProfileImage(final ProfileImage profileImage) { public void withdrawal() { this.deleted = DELETED_STATUS; + this.name = UUID.randomUUID().toString(); + this.profileImage = null; } - public void updateReliability(final List reviews) { - reliability.updateReliability(reviews); + public void updateReliability(final Reliability reliability) { + this.reliability = reliability; } } diff --git a/backend/ddang/src/main/java/com/ddang/ddang/user/domain/UserReliability.java b/backend/ddang/src/main/java/com/ddang/ddang/user/domain/UserReliability.java new file mode 100644 index 000000000..107697c4b --- /dev/null +++ b/backend/ddang/src/main/java/com/ddang/ddang/user/domain/UserReliability.java @@ -0,0 +1,71 @@ +package com.ddang.ddang.user.domain; + +import com.ddang.ddang.review.domain.Reviews; +import jakarta.persistence.AttributeOverride; +import jakarta.persistence.Column; +import jakarta.persistence.Embedded; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.ForeignKey; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.OneToOne; +import lombok.AccessLevel; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.ToString; + +@Entity +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@Getter +@EqualsAndHashCode(of = "id", callSuper = false) +@ToString(of = {"id", "reliability", "appliedReviewCount"}) +public class UserReliability { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @OneToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "user_id", foreignKey = @ForeignKey(name = "fk_user_reliability_user"), nullable = false) + private User user; + + @Embedded + @AttributeOverride(name = "value", column = @Column(name = "reliability")) + private Reliability reliability; + + private int appliedReviewCount = 0; + + public UserReliability(final User user) { + this.user = user; + this.reliability = user.getReliability(); + } + + public void updateReliability(final Reviews newReviews) { + if (newReviews.isEmpty()) { + return; + } + final Reliability newReliability = calculateNewReliability(newReviews); + + this.reliability = newReliability; + addAppliedReviewCount(newReviews.size()); + user.updateReliability(newReliability); + } + + private Reliability calculateNewReliability(final Reviews newReviews) { + final double currentReviewScoreSum = reliability.calculateReviewScoreSum(appliedReviewCount); + final double newReviewScoreSum = newReviews.addAllReviewScore(); + final int allReviewCount = appliedReviewCount + newReviews.size(); + + final double newReliabilityValue = (currentReviewScoreSum + newReviewScoreSum) / allReviewCount; + + return new Reliability(newReliabilityValue); + } + + private void addAppliedReviewCount(final int newReviewCount) { + this.appliedReviewCount += newReviewCount; + } +} diff --git a/backend/ddang/src/main/java/com/ddang/ddang/user/domain/repository/ReliabilityUpdateHistoryRepository.java b/backend/ddang/src/main/java/com/ddang/ddang/user/domain/repository/ReliabilityUpdateHistoryRepository.java new file mode 100644 index 000000000..1f16b84d8 --- /dev/null +++ b/backend/ddang/src/main/java/com/ddang/ddang/user/domain/repository/ReliabilityUpdateHistoryRepository.java @@ -0,0 +1,12 @@ +package com.ddang.ddang.user.domain.repository; + +import com.ddang.ddang.user.domain.ReliabilityUpdateHistory; + +import java.util.Optional; + +public interface ReliabilityUpdateHistoryRepository { + + ReliabilityUpdateHistory save(final ReliabilityUpdateHistory reliabilityUpdateHistory); + + Optional findFirstByOrderByIdDesc(); +} diff --git a/backend/ddang/src/main/java/com/ddang/ddang/user/domain/repository/UserReliabilityRepository.java b/backend/ddang/src/main/java/com/ddang/ddang/user/domain/repository/UserReliabilityRepository.java new file mode 100644 index 000000000..03373f6a4 --- /dev/null +++ b/backend/ddang/src/main/java/com/ddang/ddang/user/domain/repository/UserReliabilityRepository.java @@ -0,0 +1,12 @@ +package com.ddang.ddang.user.domain.repository; + +import com.ddang.ddang.user.domain.UserReliability; + +import java.util.Optional; + +public interface UserReliabilityRepository { + + UserReliability save(final UserReliability userReliability); + + Optional findByUserId(final Long userId); +} diff --git a/backend/ddang/src/main/java/com/ddang/ddang/user/domain/repository/UserRepository.java b/backend/ddang/src/main/java/com/ddang/ddang/user/domain/repository/UserRepository.java new file mode 100644 index 000000000..6f1b7fdfc --- /dev/null +++ b/backend/ddang/src/main/java/com/ddang/ddang/user/domain/repository/UserRepository.java @@ -0,0 +1,22 @@ +package com.ddang.ddang.user.domain.repository; + +import com.ddang.ddang.user.domain.User; + +import java.util.Optional; + +public interface UserRepository { + + User save(final User user); + + Optional findById(final Long id); + + boolean existsById(final Long id); + + Optional findByOauthId(final String oauthId); + + boolean existsByIdAndDeletedIsTrue(final Long id); + + boolean existsByNameEndingWith(final String name); + + boolean existsByName(final String name); +} diff --git a/backend/ddang/src/main/java/com/ddang/ddang/user/infrastructure/persistence/JpaReliabilityUpdateHistoryRepository.java b/backend/ddang/src/main/java/com/ddang/ddang/user/infrastructure/persistence/JpaReliabilityUpdateHistoryRepository.java new file mode 100644 index 000000000..c08d74b8f --- /dev/null +++ b/backend/ddang/src/main/java/com/ddang/ddang/user/infrastructure/persistence/JpaReliabilityUpdateHistoryRepository.java @@ -0,0 +1,11 @@ +package com.ddang.ddang.user.infrastructure.persistence; + +import com.ddang.ddang.user.domain.ReliabilityUpdateHistory; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.Optional; + +public interface JpaReliabilityUpdateHistoryRepository extends JpaRepository { + + Optional findFirstByOrderByIdDesc(); +} diff --git a/backend/ddang/src/main/java/com/ddang/ddang/user/infrastructure/persistence/JpaUserReliabilityRepository.java b/backend/ddang/src/main/java/com/ddang/ddang/user/infrastructure/persistence/JpaUserReliabilityRepository.java new file mode 100644 index 000000000..e85346b3a --- /dev/null +++ b/backend/ddang/src/main/java/com/ddang/ddang/user/infrastructure/persistence/JpaUserReliabilityRepository.java @@ -0,0 +1,18 @@ +package com.ddang.ddang.user.infrastructure.persistence; + +import com.ddang.ddang.user.domain.UserReliability; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; + +import java.util.Optional; + +public interface JpaUserReliabilityRepository extends JpaRepository { + + @Query(""" + SELECT ur + FROM UserReliability ur + JOIN FETCH ur.user u + WHERE u.id = :userId + """) + Optional findByUserId(final Long userId); +} diff --git a/backend/ddang/src/main/java/com/ddang/ddang/user/infrastructure/persistence/JpaUserRepository.java b/backend/ddang/src/main/java/com/ddang/ddang/user/infrastructure/persistence/JpaUserRepository.java index 390768b88..a703d934b 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/user/infrastructure/persistence/JpaUserRepository.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/user/infrastructure/persistence/JpaUserRepository.java @@ -2,16 +2,29 @@ import com.ddang.ddang.user.domain.User; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; import java.util.Optional; public interface JpaUserRepository extends JpaRepository { - Optional findByOauthIdAndDeletedIsFalse(final String oauthId); + @Query(""" + SELECT u + FROM User u + WHERE u.deleted = false AND u.id = :id + """) + Optional findById(final Long id); - Optional findByIdAndDeletedIsFalse(final Long id); + @Query(""" + SELECT u + FROM User u + WHERE u.deleted = false AND u.oauthInformation.oauthId = :oauthId + """) + Optional findByOauthId(final String oauthId); boolean existsByIdAndDeletedIsTrue(final Long id); boolean existsByNameEndingWith(final String name); + + boolean existsByName(final String name); } diff --git a/backend/ddang/src/main/java/com/ddang/ddang/user/infrastructure/persistence/ReliabilityUpdateHistoryRepositoryImpl.java b/backend/ddang/src/main/java/com/ddang/ddang/user/infrastructure/persistence/ReliabilityUpdateHistoryRepositoryImpl.java new file mode 100644 index 000000000..c5427a852 --- /dev/null +++ b/backend/ddang/src/main/java/com/ddang/ddang/user/infrastructure/persistence/ReliabilityUpdateHistoryRepositoryImpl.java @@ -0,0 +1,25 @@ +package com.ddang.ddang.user.infrastructure.persistence; + +import com.ddang.ddang.user.domain.ReliabilityUpdateHistory; +import com.ddang.ddang.user.domain.repository.ReliabilityUpdateHistoryRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Repository; + +import java.util.Optional; + +@Repository +@RequiredArgsConstructor +public class ReliabilityUpdateHistoryRepositoryImpl implements ReliabilityUpdateHistoryRepository { + + private final JpaReliabilityUpdateHistoryRepository jpaReliabilityUpdateHistoryRepository; + + @Override + public ReliabilityUpdateHistory save(final ReliabilityUpdateHistory reliabilityUpdateHistory) { + return jpaReliabilityUpdateHistoryRepository.save(reliabilityUpdateHistory); + } + + @Override + public Optional findFirstByOrderByIdDesc() { + return jpaReliabilityUpdateHistoryRepository.findFirstByOrderByIdDesc(); + } +} diff --git a/backend/ddang/src/main/java/com/ddang/ddang/user/infrastructure/persistence/UserReliabilityRepositoryImpl.java b/backend/ddang/src/main/java/com/ddang/ddang/user/infrastructure/persistence/UserReliabilityRepositoryImpl.java new file mode 100644 index 000000000..e2789739a --- /dev/null +++ b/backend/ddang/src/main/java/com/ddang/ddang/user/infrastructure/persistence/UserReliabilityRepositoryImpl.java @@ -0,0 +1,25 @@ +package com.ddang.ddang.user.infrastructure.persistence; + +import com.ddang.ddang.user.domain.UserReliability; +import com.ddang.ddang.user.domain.repository.UserReliabilityRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Repository; + +import java.util.Optional; + +@Repository +@RequiredArgsConstructor +public class UserReliabilityRepositoryImpl implements UserReliabilityRepository { + + private final JpaUserReliabilityRepository jpaUserReliabilityRepository; + + @Override + public UserReliability save(final UserReliability userReliability) { + return jpaUserReliabilityRepository.save(userReliability); + } + + @Override + public Optional findByUserId(final Long userId) { + return jpaUserReliabilityRepository.findByUserId(userId); + } +} diff --git a/backend/ddang/src/main/java/com/ddang/ddang/user/infrastructure/persistence/UserRepositoryImpl.java b/backend/ddang/src/main/java/com/ddang/ddang/user/infrastructure/persistence/UserRepositoryImpl.java new file mode 100644 index 000000000..5a5e3fac6 --- /dev/null +++ b/backend/ddang/src/main/java/com/ddang/ddang/user/infrastructure/persistence/UserRepositoryImpl.java @@ -0,0 +1,50 @@ +package com.ddang.ddang.user.infrastructure.persistence; + +import com.ddang.ddang.user.domain.User; +import com.ddang.ddang.user.domain.repository.UserRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Repository; + +import java.util.Optional; + +@Repository +@RequiredArgsConstructor +public class UserRepositoryImpl implements UserRepository { + + private final JpaUserRepository jpaUserRepository; + + @Override + public User save(final User user) { + return jpaUserRepository.save(user); + } + + @Override + public Optional findById(final Long id) { + return jpaUserRepository.findById(id); + } + + @Override + public boolean existsById(final Long id) { + return jpaUserRepository.existsById(id); + } + + @Override + public Optional findByOauthId(final String oauthId) { + return jpaUserRepository.findByOauthId(oauthId); + } + + @Override + public boolean existsByIdAndDeletedIsTrue(final Long id) { + return jpaUserRepository.existsByIdAndDeletedIsTrue(id); + } + + @Override + public boolean existsByNameEndingWith(final String name) { + return jpaUserRepository.existsByNameEndingWith(name); + } + + @Override + public boolean existsByName(final String name) { + return jpaUserRepository.existsByName(name); + } +} diff --git a/backend/ddang/src/main/resources/application-local.yml b/backend/ddang/src/main/resources/application-local.yml index 2db9e094b..fd586f349 100644 --- a/backend/ddang/src/main/resources/application-local.yml +++ b/backend/ddang/src/main/resources/application-local.yml @@ -29,10 +29,6 @@ data: init: region: enabled: false - auction: - enabled: false - user: - enabled: false image: store: diff --git a/backend/ddang/src/main/resources/db/migration/V17__create_reliability_tables.sql b/backend/ddang/src/main/resources/db/migration/V17__create_reliability_tables.sql new file mode 100644 index 000000000..91ce85f37 --- /dev/null +++ b/backend/ddang/src/main/resources/db/migration/V17__create_reliability_tables.sql @@ -0,0 +1,18 @@ +create table reliability_update_history +( + id bigint not null auto_increment, + created_time datetime(6) not null, + last_applied_review_id bigint, + primary key (id) +); + +create table user_reliability +( + id bigint not null auto_increment, + applied_review_count integer not null, + reliability float(53), + user_id bigint not null, + primary key (id) +); + +alter table user_reliability add constraint fk_user_reliability_user foreign key (user_id) references users (id); diff --git a/backend/ddang/src/main/resources/db/migration/V18__alter_user_oauth_type_tables.sql b/backend/ddang/src/main/resources/db/migration/V18__alter_user_oauth_type_tables.sql new file mode 100644 index 000000000..1a614bb22 --- /dev/null +++ b/backend/ddang/src/main/resources/db/migration/V18__alter_user_oauth_type_tables.sql @@ -0,0 +1,3 @@ +ALTER TABLE users ADD oauth2_type VARCHAR(10); +UPDATE users SET oauth2_type = 'KAKAO' WHERE oauth2_type is null; +ALTER TABLE users MODIFY oauth2_type VARCHAR(10) NOT NULL; diff --git a/backend/ddang/src/main/resources/db/migration/V19__alter_user_tables.sql b/backend/ddang/src/main/resources/db/migration/V19__alter_user_tables.sql new file mode 100644 index 000000000..1a844adae --- /dev/null +++ b/backend/ddang/src/main/resources/db/migration/V19__alter_user_tables.sql @@ -0,0 +1 @@ +ALTER TABLE users MODIFY profile_image_id bigint; diff --git a/backend/ddang/src/main/resources/db/migration/V20__alter_answer_tables.sql b/backend/ddang/src/main/resources/db/migration/V20__alter_answer_tables.sql new file mode 100644 index 000000000..b5da309ae --- /dev/null +++ b/backend/ddang/src/main/resources/db/migration/V20__alter_answer_tables.sql @@ -0,0 +1,2 @@ +alter table answer add writer_id bigint; +alter table answer add constraint fk_answer_writer foreign key (writer_id) references users (id); diff --git a/backend/ddang/src/main/resources/static/docs/docs.html b/backend/ddang/src/main/resources/static/docs/docs.html index ecfe045b1..bf5f56834 100644 --- a/backend/ddang/src/main/resources/static/docs/docs.html +++ b/backend/ddang/src/main/resources/static/docs/docs.html @@ -654,7 +654,8 @@

응답

{ "accessToken" : "Bearer accessToken", - "refreshToken" : "Bearer refreshToken" + "refreshToken" : "Bearer refreshToken", + "isSignUpUser" : false } @@ -682,6 +683,11 @@

응답

String

Refresh Token

+ +

isSignUpUser

+

Boolean

+

최초 로그인 여부(회원가입)

+ @@ -898,7 +904,7 @@

탈퇴

요청

-
POST /oauth2/withdrawal/kakao HTTP/1.1
+
POST /oauth2/withdrawal HTTP/1.1
 Content-Type: application/json
 Authorization: Bearer accessToken
 
@@ -907,25 +913,9 @@ 

요청

}
- - ---- - - - - - - - - - - - - -
Table 2. /oauth2/withdrawal/{oauth2Type}
ParameterDescription

oauth2Type

소셜 로그인을 할 서비스 선택(kakao로 고정)

+
+

Unresolved directive in docs.adoc - include::/Users/labtop/intellij/test-ddang/2023-3-ddang/backend/ddang/build/generated-snippets/authentication-controller-test/ouath2-type과_access-token과_refresh-token을_전달하면_탈퇴한다/path-parameters.adoc[]

+
@@ -1288,7 +1278,7 @@

요청

- +@@ -1367,7 +1357,7 @@

요청

Table 3. /regions/{firstId}Table 2. /regions/{firstId}
- +@@ -1463,7 +1453,7 @@

요청

Content-Disposition: form-data; name=request; filename=request Content-Type: application/json -{"title":"제목","description":"내용","bidUnit":1000,"startPrice":1000,"closingTime":"2023-10-12T11:02:42.5505268","subCategoryId":2,"thirdRegionIds":[3]} +{"title":"제목","description":"내용","bidUnit":1000,"startPrice":1000,"closingTime":"2023-10-18T21:12:06.450773","subCategoryId":2,"thirdRegionIds":[3]} --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm-- @@ -2012,7 +2002,7 @@

요청

Table 4. /regions/{firstId}/{secondId}Table 3. /regions/{firstId}/{secondId}
- +@@ -2044,7 +2034,7 @@

요청

- +
Table 5. /auctions/{auctionId}Table 4. /auctions/{auctionId}

Authorization

회원 Bearer 인증 정보

사용자 Bearer 인증 정보

@@ -2070,8 +2060,8 @@

응답

"lastBidPrice" : null, "status" : "UNBIDDEN", "bidUnit" : 1000, - "registerTime" : "2023-10-09T11:02:42", - "closingTime" : "2023-10-09T11:02:42", + "registerTime" : "2023-10-15T21:12:06", + "closingTime" : "2023-10-15T21:12:06", "directRegions" : [ { "first" : "서울특별시", "second" : "강서구", @@ -2089,7 +2079,8 @@

응답

"id" : 1, "isChatParticipant" : true }, - "isOwner" : true + "isOwner" : true, + "isLastBidder" : false }
@@ -2235,12 +2226,17 @@

응답

chat.isChatParticipant

Boolean

-

채팅방을 생성 가능 유저 여부

+

로그인한 사용자가 채팅방 생성이 가능한 사용자인지에 대한 여부

isOwner

Boolean

-

유저가 해당 경매 글을 작성한 유저인지에 대한 여부

+

로그인한 사용자가 해당 경매 글을 작성한 사용자인지에 대한 여부

+ + +

isLastBidder

+

Boolean

+

로그인한 사용자가 해당 경매의 최고 입찰자인지에 대한 여부

@@ -2258,7 +2254,7 @@

요청

- +@@ -2409,7 +2405,7 @@

요청

Table 6. /auctions/{auctionId}Table 5. /auctions/{auctionId}
- +@@ -2537,7 +2533,7 @@

요청

Table 7. /questions/{questionId}Table 6. /questions/{questionId}
- +@@ -2593,8 +2589,9 @@

응답

"name" : "질문자", "image" : "http://localhost:8080/users/images/1" }, - "createdTime" : "2023-10-09T11:02:42", - "content" : "질문1" + "createdTime" : "2023-10-15T21:12:06", + "content" : "질문1", + "isQuestioner" : false }, "answer" : { "id" : 1, @@ -2603,7 +2600,7 @@

응답

"name" : "판매자", "image" : "http://localhost:8080/users/images/2" }, - "createdTime" : "2023-10-09T11:02:42", + "createdTime" : "2023-10-15T21:12:06", "content" : "답변1" } }, { @@ -2614,8 +2611,9 @@

응답

"name" : "질문자", "image" : "http://localhost:8080/users/images/1" }, - "createdTime" : "2023-10-09T11:02:42", - "content" : "질문2" + "createdTime" : "2023-10-15T21:12:06", + "content" : "질문2", + "isQuestioner" : false }, "answer" : { "id" : 2, @@ -2624,7 +2622,7 @@

응답

"name" : "판매자", "image" : "http://localhost:8080/users/images/2" }, - "createdTime" : "2023-10-09T11:02:42", + "createdTime" : "2023-10-15T21:12:06", "content" : "답변1" } } ] @@ -2691,6 +2689,11 @@

응답

+ + + + + @@ -2828,19 +2831,17 @@

응답

HTTP/1.1 200 OK
 Content-Type: application/json
 
-{
-  "bids" : [ {
-    "name" : "사용자1",
-    "profileImage" : "http://localhost:8080/users/images/1",
-    "price" : 10000,
-    "bidTime" : "2023-10-09T11:02:50"
-  }, {
-    "name" : "사용자2",
-    "profileImage" : "http://localhost:8080/users/images/2",
-    "price" : 12000,
-    "bidTime" : "2023-10-09T11:02:50"
-  } ]
-}
+[ { + "name" : "사용자1", + "profileImage" : "http://localhost:8080/users/images/1", + "price" : 10000, + "bidTime" : "2023-10-15T21:12:13" +}, { + "name" : "사용자2", + "profileImage" : "http://localhost:8080/users/images/2", + "price" : 12000, + "bidTime" : "2023-10-15T21:12:13" +} ]
Table 8. /questions/answers/{answerId}Table 7. /questions/answers/{answerId}

질문 내용

qnas.[].question.isQuestioner

Boolean

질문 작성자 여부 확인

qnas.[].answer

Object

답변 정보 JSON

@@ -2858,27 +2859,27 @@

응답

- + - + - + - + - + @@ -3033,7 +3034,7 @@

응답

"price" : 10000 }, "lastMessage" : { - "createdAt" : "2023-10-09T11:02:52", + "createdAt" : "2023-10-15T21:12:16", "contents" : "메시지1" }, "isChatAvailable" : true @@ -3051,7 +3052,7 @@

응답

"price" : 20000 }, "lastMessage" : { - "createdAt" : "2023-10-09T11:02:52", + "createdAt" : "2023-10-15T21:12:16", "contents" : "메시지2" }, "isChatAvailable" : true @@ -3163,7 +3164,7 @@

요청

bids.[]

[]

Array

특정 경매의 모든 입찰 목록

bids.[].name

[].name

String

입찰한 사용자의 닉네임

bids.[].profileImage

[].profileImage

String

입찰한 사용자의 프로필 이미지 URL

bids.[].price

[].price

Number

입찰한 금액

bids.[].bidTime

[].bidTime

String

입찰한 시간

- +@@ -3314,7 +3315,7 @@

요청

Table 9. /chattings/{chatRoomId}Table 8. /chattings/{chatRoomId}
- +@@ -3425,7 +3426,7 @@

요청

Table 10. /chattings/{chatRoomId}/messagesTable 9. /chattings/{chatRoomId}/messages
- +@@ -3489,7 +3490,7 @@

응답

[ { "id" : 1, - "createdAt" : "2023-10-09T11:02:52", + "createdAt" : "2023-10-15T21:12:16", "isMyMessage" : true, "contents" : "메시지내용" } ] @@ -3639,7 +3640,7 @@

응답

"id" : 2, "name" : "회원1" }, - "createdTime" : "2023-10-09T11:03:02", + "createdTime" : "2023-10-15T21:12:26", "auction" : { "id" : 1, "title" : "제목" @@ -3651,7 +3652,7 @@

응답

"id" : 3, "name" : "회원2" }, - "createdTime" : "2023-10-09T11:03:02", + "createdTime" : "2023-10-15T21:12:26", "auction" : { "id" : 1, "title" : "제목" @@ -3663,7 +3664,7 @@

응답

"id" : 4, "name" : "회원3" }, - "createdTime" : "2023-10-09T11:03:02", + "createdTime" : "2023-10-15T21:12:26", "auction" : { "id" : 1, "title" : "제목" @@ -3827,7 +3828,7 @@

응답

"id" : 2, "name" : "구매자1" }, - "createdTime" : "2023-10-09T11:03:02", + "createdTime" : "2023-10-15T21:12:27", "chatRoom" : { "id" : 1 }, @@ -3838,7 +3839,7 @@

응답

"id" : 3, "name" : "구매자2" }, - "createdTime" : "2023-10-09T11:03:02", + "createdTime" : "2023-10-15T21:12:27", "chatRoom" : { "id" : 1 }, @@ -3849,7 +3850,7 @@

응답

"id" : 3, "name" : "구매자2" }, - "createdTime" : "2023-10-09T11:03:02", + "createdTime" : "2023-10-15T21:12:27", "chatRoom" : { "id" : 1 }, @@ -4013,7 +4014,7 @@

응답

"id" : 2, "name" : "구매자1" }, - "createdTime" : "2023-10-09T11:03:02", + "createdTime" : "2023-10-15T21:12:27", "question" : { "id" : 1 }, @@ -4024,7 +4025,7 @@

응답

"id" : 2, "name" : "구매자1" }, - "createdTime" : "2023-10-09T11:03:02", + "createdTime" : "2023-10-15T21:12:27", "question" : { "id" : 2 }, @@ -4035,7 +4036,7 @@

응답

"id" : 2, "name" : "구매자1" }, - "createdTime" : "2023-10-09T11:03:02", + "createdTime" : "2023-10-15T21:12:27", "question" : { "id" : 3 }, @@ -4199,7 +4200,7 @@

응답

"id" : 2, "name" : "구매자1" }, - "createdTime" : "2023-10-09T11:03:02", + "createdTime" : "2023-10-15T21:12:27", "answer" : { "id" : 1 }, @@ -4210,7 +4211,7 @@

응답

"id" : 2, "name" : "구매자1" }, - "createdTime" : "2023-10-09T11:03:02", + "createdTime" : "2023-10-15T21:12:27", "answer" : { "id" : 2 }, @@ -4221,7 +4222,7 @@

응답

"id" : 2, "name" : "구매자1" }, - "createdTime" : "2023-10-09T11:03:02", + "createdTime" : "2023-10-15T21:12:27", "answer" : { "id" : 3 }, @@ -4451,7 +4452,7 @@

요청

Table 11. /chattings/{chatRoomId}/messagesTable 10. /chattings/{chatRoomId}/messages
- +@@ -4522,7 +4523,7 @@

요청

Table 12. /reviews/{reviewId}Table 11. /reviews/{reviewId}
- +@@ -4557,7 +4558,7 @@

응답

}, "content" : "친절하다.", "score" : 5.0, - "createdTime" : "2023-10-09T11:03:03" + "createdTime" : "2023-10-15T21:12:29" }, { "id" : 2, "writer" : { @@ -4567,7 +4568,7 @@

응답

}, "content" : "친절하다.", "score" : 5.0, - "createdTime" : "2023-10-09T11:03:03" + "createdTime" : "2023-10-15T21:12:29" } ] @@ -4641,7 +4642,7 @@

요청

Table 13. /reviews/users/{userId}Table 12. /reviews/users/{userId}
- +@@ -4725,7 +4726,7 @@

응답

diff --git a/backend/ddang/src/test/java/com/ddang/ddang/auction/application/fixture/AuctionServiceFixture.java b/backend/ddang/src/test/java/com/ddang/ddang/auction/application/fixture/AuctionServiceFixture.java index 271e86d44..7f52a1832 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/auction/application/fixture/AuctionServiceFixture.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/auction/application/fixture/AuctionServiceFixture.java @@ -2,19 +2,20 @@ import com.ddang.ddang.auction.application.dto.CreateAuctionDto; import com.ddang.ddang.auction.domain.Auction; -import com.ddang.ddang.auction.infrastructure.persistence.JpaAuctionRepository; +import com.ddang.ddang.auction.domain.repository.AuctionRepository; import com.ddang.ddang.bid.domain.Bid; import com.ddang.ddang.bid.domain.BidPrice; -import com.ddang.ddang.bid.infrastructure.persistence.JpaBidRepository; +import com.ddang.ddang.bid.domain.repository.BidRepository; import com.ddang.ddang.category.domain.Category; import com.ddang.ddang.category.infrastructure.persistence.JpaCategoryRepository; import com.ddang.ddang.image.domain.ProfileImage; import com.ddang.ddang.image.domain.dto.StoreImageDto; +import com.ddang.ddang.region.domain.AuctionRegion; import com.ddang.ddang.region.domain.Region; -import com.ddang.ddang.region.infrastructure.persistence.JpaRegionRepository; +import com.ddang.ddang.region.domain.repository.RegionRepository; import com.ddang.ddang.user.domain.Reliability; import com.ddang.ddang.user.domain.User; -import com.ddang.ddang.user.infrastructure.persistence.JpaUserRepository; +import com.ddang.ddang.user.domain.repository.UserRepository; import org.junit.jupiter.api.BeforeEach; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; @@ -27,19 +28,19 @@ public class AuctionServiceFixture { @Autowired - private JpaAuctionRepository auctionRepository; + private AuctionRepository auctionRepository; @Autowired - private JpaRegionRepository regionRepository; + private RegionRepository regionRepository; @Autowired private JpaCategoryRepository categoryRepository; @Autowired - private JpaUserRepository userRepository; + private UserRepository userRepository; @Autowired - private JpaBidRepository bidRepository; + private BidRepository bidRepository; private Category 가구_카테고리 = new Category("가구"); private Category 가구_서브_의자_카테고리 = new Category("의자"); @@ -51,11 +52,11 @@ public class AuctionServiceFixture { .oauthId("12345") .build(); protected User 구매자 = User.builder() - .name("구매자") - .profileImage(new ProfileImage("upload.png", "store.png")) - .reliability(new Reliability(4.7d)) - .oauthId("54321") - .build(); + .name("구매자") + .profileImage(new ProfileImage("upload.png", "store.png")) + .reliability(new Reliability(4.7d)) + .oauthId("54321") + .build(); private MockMultipartFile 경매_이미지_파일 = new MockMultipartFile( "image.png", @@ -102,7 +103,8 @@ void setUp() { categoryRepository.save(가구_카테고리); - userRepository.saveAll(List.of(판매자, 구매자)); + userRepository.save(판매자); + userRepository.save(구매자); 유효한_경매_생성_dto = new CreateAuctionDto( "제목", @@ -212,11 +214,20 @@ void setUp() { 구매자가_입찰한_경매1_입찰 = new Bid(구매자가_입찰한_경매1, 구매자, 구매자가_입찰한_경매1_입찰_가격); 구매자가_입찰한_경매2_입찰 = new Bid(구매자가_입찰한_경매2, 구매자, 구매자가_입찰한_경매2_입찰_가격); - bidRepository.saveAll(List.of(구매자가_입찰한_경매1_입찰, 구매자가_입찰한_경매2_입찰)); + bidRepository.save(구매자가_입찰한_경매1_입찰); + bidRepository.save(구매자가_입찰한_경매2_입찰); 구매자가_입찰한_경매1.updateLastBid(구매자가_입찰한_경매1_입찰); 구매자가_입찰한_경매2.updateLastBid(구매자가_입찰한_경매2_입찰); - auctionRepository.saveAll(List.of(구매자가_입찰한_경매1, 구매자가_입찰한_경매2, 종료되는_날이_3일_뒤인_경매, 종료된_경매)); + 구매자가_입찰한_경매1.addAuctionRegions(List.of(new AuctionRegion(역삼동))); + 구매자가_입찰한_경매2.addAuctionRegions(List.of(new AuctionRegion(역삼동))); + 종료되는_날이_3일_뒤인_경매.addAuctionRegions(List.of(new AuctionRegion(역삼동))); + 종료된_경매.addAuctionRegions(List.of(new AuctionRegion(역삼동))); + + auctionRepository.save(구매자가_입찰한_경매1); + auctionRepository.save(구매자가_입찰한_경매2); + auctionRepository.save(종료되는_날이_3일_뒤인_경매); + auctionRepository.save(종료된_경매); } } diff --git a/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/AuctionRepositoryImplTest.java b/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/AuctionRepositoryImplTest.java new file mode 100644 index 000000000..e170dd59a --- /dev/null +++ b/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/AuctionRepositoryImplTest.java @@ -0,0 +1,242 @@ +package com.ddang.ddang.auction.infrastructure.persistence; + +import com.ddang.ddang.auction.domain.Auction; +import com.ddang.ddang.auction.domain.repository.AuctionRepository; +import com.ddang.ddang.auction.infrastructure.persistence.fixture.AuctionRepositoryImplFixture; +import com.ddang.ddang.configuration.JpaConfiguration; +import com.ddang.ddang.configuration.QuerydslConfiguration; +import com.querydsl.jpa.impl.JPAQueryFactory; +import org.assertj.core.api.SoftAssertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.context.annotation.Import; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Slice; + +import java.util.Optional; + +import static org.assertj.core.api.Assertions.assertThat; + +@DataJpaTest +@Import({JpaConfiguration.class, QuerydslConfiguration.class}) +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) +@SuppressWarnings("NonAsciiCharacters") +class AuctionRepositoryImplTest extends AuctionRepositoryImplFixture { + + @Autowired + JpaAuctionRepository jpaAuctionRepository; + + AuctionRepository auctionRepository; + + @BeforeEach + void setUp(@Autowired final JPAQueryFactory jpaQueryFactory) { + auctionRepository = new AuctionRepositoryImpl( + jpaAuctionRepository, + new QuerydslAuctionRepository(jpaQueryFactory) + ); + } + + @Test + void 경매를_저장한다() { + // when + final Auction actual = auctionRepository.save(저장하기_전_경매_엔티티); + + // then + assertThat(actual.getId()).isPositive(); + } + + @Test + void 지정한_id의_경매가_존재하는_경우_true를_반환한다() { + // when + final boolean actual = auctionRepository.existsById(저장된_경매_엔티티.getId()); + + // then + assertThat(actual).isTrue(); + } + + @Test + void 지정한_id의_경매가_존재하지_않는_경우_false를_반환한다() { + // when + final boolean actual = auctionRepository.existsById(존재하지_않는_경매_id); + + // then + assertThat(actual).isFalse(); + } + + @Test + void 지정한_아이디에_대한_경매와_관련된_데이터를_모두_조회한다() { + // when + final Optional actual = auctionRepository.findTotalAuctionById(저장된_경매_엔티티.getId()); + + // then + SoftAssertions.assertSoftly(softAssertions -> { + softAssertions.assertThat(actual).isPresent(); + softAssertions.assertThat(actual.get().getId()).isEqualTo(저장된_경매_엔티티.getId()); + softAssertions.assertThat(actual.get().getTitle()).isEqualTo(저장된_경매_엔티티.getTitle()); + softAssertions.assertThat(actual.get().getDescription()).isEqualTo(저장된_경매_엔티티.getDescription()); + softAssertions.assertThat(actual.get().getBidUnit()).isEqualTo(저장된_경매_엔티티.getBidUnit()); + softAssertions.assertThat(actual.get().getStartPrice()).isEqualTo(저장된_경매_엔티티.getStartPrice()); + softAssertions.assertThat(actual.get().getClosingTime()).isEqualTo(저장된_경매_엔티티.getClosingTime()); + softAssertions.assertThat(actual.get().getAuctionRegions()).isNotNull(); + softAssertions.assertThat(actual.get().getAuctionRegions().get(0)).isNotNull(); + softAssertions.assertThat(actual.get().getAuctionRegions().get(0).getThirdRegion()).isNotNull(); + softAssertions.assertThat(actual.get().getAuctionRegions().get(0).getThirdRegion().getFirstRegion()) + .isNotNull(); + softAssertions.assertThat(actual.get().getAuctionRegions().get(0).getThirdRegion().getSecondRegion()) + .isNotNull(); + softAssertions.assertThat(actual.get().getSubCategory()).isNotNull(); + softAssertions.assertThat(actual.get().getSubCategory().getMainCategory()).isNotNull(); + softAssertions.assertThat(actual.get().getSeller()).isNotNull(); + }); + } + + @Test + void 지정한_아이디에_대한_경매를_조회한다() { + // when + final Optional actual = auctionRepository.findPureAuctionById(저장된_경매_엔티티.getId()); + + // then + SoftAssertions.assertSoftly(softAssertions -> { + softAssertions.assertThat(actual).isPresent(); + softAssertions.assertThat(actual.get().getId()).isEqualTo(저장된_경매_엔티티.getId()); + softAssertions.assertThat(actual.get().getTitle()).isEqualTo(저장된_경매_엔티티.getTitle()); + softAssertions.assertThat(actual.get().getDescription()).isEqualTo(저장된_경매_엔티티.getDescription()); + softAssertions.assertThat(actual.get().getBidUnit()).isEqualTo(저장된_경매_엔티티.getBidUnit()); + softAssertions.assertThat(actual.get().getStartPrice()).isEqualTo(저장된_경매_엔티티.getStartPrice()); + softAssertions.assertThat(actual.get().getClosingTime()).isEqualTo(저장된_경매_엔티티.getClosingTime()); + }); + } + + @Test + void 삭제된_아이디에_대한_경매_조회시_빈_optional을_반환한다() { + // when + final Optional actual = auctionRepository.findTotalAuctionById(삭제된_경매_엔티티.getId()); + + // then + assertThat(actual).isEmpty(); + } + + @Test + void 경매_목록을_조회한다() { + // when + final Slice actual = auctionRepository.findAuctionsAllByCondition( + 검색어_없음, + PageRequest.of(페이지_1, 페이지_크기) + ); + + // then + SoftAssertions.assertSoftly(softAssertions -> { + assertThat(actual).hasSize(1); + assertThat(actual.getContent().get(0)).isEqualTo(저장된_경매_엔티티); + assertThat(actual.hasNext()).isFalse(); + }); + } + + @Test + void 사용자가_등록한_경매_목록을_조회한다() { + // when + final Slice actual = auctionRepository.findAuctionsAllByUserId( + 판매자.getId(), + PageRequest.of(페이지_1, 페이지_크기) + ); + + // then + SoftAssertions.assertSoftly(softAssertions -> { + assertThat(actual).hasSize(1); + assertThat(actual.getContent().get(0)).isEqualTo(저장된_경매_엔티티); + assertThat(actual.hasNext()).isFalse(); + }); + } + + @Test + void 사용자가_입찰한_경매_목록을_조회한다() { + // when + final Slice actual = auctionRepository.findAuctionsAllByBidderId( + 구매자.getId(), + PageRequest.of(페이지_1, 페이지_크기) + ); + + // then + SoftAssertions.assertSoftly(softAssertions -> { + assertThat(actual).hasSize(1); + assertThat(actual.getContent().get(0)).isEqualTo(저장된_경매_엔티티); + assertThat(actual.hasNext()).isFalse(); + }); + } + + @Test + void 특정_사용자가_판매자인_경매중_현재_진행_중인_경매가_있다면_참을_반환한다() { + // when + final boolean actual = auctionRepository.existsBySellerIdAndAuctionStatusIsOngoing( + 판매자.getId(), + 저장된_경매_엔티티.getClosingTime().minusDays(1) + ); + + // then + assertThat(actual).isTrue(); + } + + @Test + void 특정_사용자가_판매자인_경매중_현재_진행_중인_경매가_없다면_거짓을_반환한다() { + // when + final boolean actual = auctionRepository.existsBySellerIdAndAuctionStatusIsOngoing( + 판매자.getId(), + 저장된_경매_엔티티.getClosingTime().plusDays(1) + ); + + // then + assertThat(actual).isFalse(); + } + + @Test + void 특정_사용자가_판매자인_경매중_현재_진행_중인_경매가_있지만_해당_경매가_삭제_됐다면_거짓을_반환한다() { + // when + final boolean actual = auctionRepository.existsBySellerIdAndAuctionStatusIsOngoing( + 삭제한_경매를_갖고_있는_판매자.getId(), + 삭제된_경매만_있는_사용자의_삭제된_경매_엔티티.getClosingTime().minusDays(1) + ); + + // then + assertThat(actual).isFalse(); + } + + @Test + void 특정_사용자가_마지막_입찰자인_경매중_현재_진행_중인_경매가_있다면_참을_반환한다() { + // when + final boolean actual = auctionRepository.existsLastBidByUserIdAndAuctionStatusIsOngoing( + 구매자.getId(), + 저장된_경매_엔티티.getClosingTime().minusDays(1) + ); + + // then + assertThat(actual).isTrue(); + } + + @Test + void 특정_사용자가_마지막_입찰자인_경매중_현재_진행_중인_경매가_없다면_거짓을_반환한다() { + // when + final boolean actual = auctionRepository.existsLastBidByUserIdAndAuctionStatusIsOngoing( + 구매자.getId(), + 저장된_경매_엔티티.getClosingTime().plusDays(1) + ); + + // then + assertThat(actual).isFalse(); + } + + @Test + void 특정_사용자가_마지막_입찰자인_경매중_현재_진행_중인_경매가_없지만_삭제_됐다면_거짓을_반환한다() { + // when + final boolean actual = auctionRepository.existsLastBidByUserIdAndAuctionStatusIsOngoing( + 삭제된_경매의_마지막_입찰자.getId(), + 삭제된_경매_엔티티.getClosingTime().minusDays(1) + ); + + // then + assertThat(actual).isFalse(); + } +} diff --git a/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/JpaAuctionRepositoryTest.java b/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/JpaAuctionRepositoryTest.java index dc415434e..30ab5239e 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/JpaAuctionRepositoryTest.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/JpaAuctionRepositoryTest.java @@ -1,9 +1,14 @@ package com.ddang.ddang.auction.infrastructure.persistence; +import static org.assertj.core.api.Assertions.assertThat; + import com.ddang.ddang.auction.domain.Auction; import com.ddang.ddang.auction.infrastructure.persistence.fixture.JpaAuctionRepositoryFixture; import com.ddang.ddang.configuration.JpaConfiguration; import com.ddang.ddang.configuration.QuerydslConfiguration; + +import java.util.Optional; + import org.assertj.core.api.SoftAssertions; import org.junit.jupiter.api.DisplayNameGeneration; import org.junit.jupiter.api.DisplayNameGenerator; @@ -12,10 +17,6 @@ import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import org.springframework.context.annotation.Import; -import java.util.Optional; - -import static org.assertj.core.api.Assertions.assertThat; - @DataJpaTest @Import({JpaConfiguration.class, QuerydslConfiguration.class}) @DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) @@ -30,13 +31,59 @@ class JpaAuctionRepositoryTest extends JpaAuctionRepositoryFixture { // when final Auction actual = auctionRepository.save(저장하기_전_경매_엔티티); + // then assertThat(actual.getId()).isPositive(); } + @Test + void 지정한_id의_경매가_존재하는_경우_true를_반환한다() { + // when + final boolean actual = auctionRepository.existsById(저장된_경매_엔티티.getId()); + + // then + assertThat(actual).isTrue(); + } + + @Test + void 지정한_id의_경매가_존재하지_않는_경우_false를_반환한다() { + // when + final boolean actual = auctionRepository.existsById(존재하지_않는_경매_id); + + // then + assertThat(actual).isFalse(); + } + + @Test + void 지정한_아이디에_대한_경매와_관련된_데이터를_모두_조회한다() { + // when + final Optional actual = auctionRepository.findTotalAuctionById(저장된_경매_엔티티.getId()); + + // then + SoftAssertions.assertSoftly(softAssertions -> { + softAssertions.assertThat(actual).isPresent(); + softAssertions.assertThat(actual.get().getId()).isEqualTo(저장된_경매_엔티티.getId()); + softAssertions.assertThat(actual.get().getTitle()).isEqualTo(저장된_경매_엔티티.getTitle()); + softAssertions.assertThat(actual.get().getDescription()).isEqualTo(저장된_경매_엔티티.getDescription()); + softAssertions.assertThat(actual.get().getBidUnit()).isEqualTo(저장된_경매_엔티티.getBidUnit()); + softAssertions.assertThat(actual.get().getStartPrice()).isEqualTo(저장된_경매_엔티티.getStartPrice()); + softAssertions.assertThat(actual.get().getClosingTime()).isEqualTo(저장된_경매_엔티티.getClosingTime()); + softAssertions.assertThat(actual.get().getAuctionRegions()).isNotNull(); + softAssertions.assertThat(actual.get().getAuctionRegions().get(0)).isNotNull(); + softAssertions.assertThat(actual.get().getAuctionRegions().get(0).getThirdRegion()).isNotNull(); + softAssertions.assertThat(actual.get().getAuctionRegions().get(0).getThirdRegion().getFirstRegion()) + .isNotNull(); + softAssertions.assertThat(actual.get().getAuctionRegions().get(0).getThirdRegion().getSecondRegion()) + .isNotNull(); + softAssertions.assertThat(actual.get().getSubCategory()).isNotNull(); + softAssertions.assertThat(actual.get().getSubCategory().getMainCategory()).isNotNull(); + softAssertions.assertThat(actual.get().getSeller()).isNotNull(); + }); + } + @Test void 지정한_아이디에_대한_경매를_조회한다() { // when - final Optional actual = auctionRepository.findByIdAndDeletedIsFalse(저장된_경매_엔티티.getId()); + final Optional actual = auctionRepository.findPureAuctionById(저장된_경매_엔티티.getId()); // then SoftAssertions.assertSoftly(softAssertions -> { @@ -53,9 +100,81 @@ class JpaAuctionRepositoryTest extends JpaAuctionRepositoryFixture { @Test void 삭제된_아이디에_대한_경매_조회시_빈_optional을_반환한다() { // when - final Optional actual = auctionRepository.findByIdAndDeletedIsFalse(삭제된_경매_엔티티.getId()); + final Optional actual = auctionRepository.findTotalAuctionById(삭제된_경매_엔티티.getId()); // then assertThat(actual).isEmpty(); } + + @Test + void 특정_사용자가_판매자인_경매중_현재_진행_중인_경매가_있다면_참을_반환한다() { + // when + final boolean actual = auctionRepository.existsBySellerIdAndDeletedIsFalseAndClosingTimeGreaterThanEqual( + 판매자.getId(), + 저장된_경매_엔티티.getClosingTime().minusDays(1) + ); + + // then + assertThat(actual).isTrue(); + } + + @Test + void 특정_사용자가_판매자인_경매중_현재_진행_중인_경매가_없다면_거짓을_반환한다() { + // when + final boolean actual = auctionRepository.existsBySellerIdAndDeletedIsFalseAndClosingTimeGreaterThanEqual( + 판매자.getId(), + 저장된_경매_엔티티.getClosingTime().plusDays(1) + ); + + // then + assertThat(actual).isFalse(); + } + + @Test + void 특정_사용자가_판매자인_경매중_현재_진행_중인_경매가_있지만_해당_경매가_삭제_됐다면_거짓을_반환한다() { + // when + final boolean actual = auctionRepository.existsBySellerIdAndDeletedIsFalseAndClosingTimeGreaterThanEqual( + 삭제한_경매를_갖고_있는_판매자.getId(), + 삭제된_경매만_있는_사용자의_삭제된_경매_엔티티.getClosingTime().minusDays(1) + ); + + // then + assertThat(actual).isFalse(); + } + + @Test + void 특정_사용자가_마지막_입찰자인_경매중_현재_진행_중인_경매가_있다면_참을_반환한다() { + // when + final boolean actual = auctionRepository.existsByLastBidBidderIdAndDeletedIsFalseAndClosingTimeGreaterThanEqual( + 입찰자.getId(), + 저장된_경매_엔티티.getClosingTime().minusDays(1) + ); + + // then + assertThat(actual).isTrue(); + } + + @Test + void 특정_사용자가_마지막_입찰자인_경매중_현재_진행_중인_경매가_없다면_거짓을_반환한다() { + // when + final boolean actual = auctionRepository.existsByLastBidBidderIdAndDeletedIsFalseAndClosingTimeGreaterThanEqual( + 입찰자.getId(), + 저장된_경매_엔티티.getClosingTime().plusDays(1) + ); + + // then + assertThat(actual).isFalse(); + } + + @Test + void 특정_사용자가_마지막_입찰자인_경매중_현재_진행_중인_경매가_없지만_삭제_됐다면_거짓을_반환한다() { + // when + final boolean actual = auctionRepository.existsByLastBidBidderIdAndDeletedIsFalseAndClosingTimeGreaterThanEqual( + 삭제된_경매의_마지막_입찰자.getId(), + 삭제된_경매_엔티티.getClosingTime().minusDays(1) + ); + + // then + assertThat(actual).isFalse(); + } } diff --git a/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/QuerydslAuctionAndImageRepositoryImplTest.java b/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/QuerydslAuctionAndImageRepositoryTest.java similarity index 64% rename from backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/QuerydslAuctionAndImageRepositoryImplTest.java rename to backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/QuerydslAuctionAndImageRepositoryTest.java index 43d57c08e..eb4847e87 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/QuerydslAuctionAndImageRepositoryImplTest.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/QuerydslAuctionAndImageRepositoryTest.java @@ -1,11 +1,13 @@ package com.ddang.ddang.auction.infrastructure.persistence; -import com.ddang.ddang.auction.infrastructure.persistence.dto.AuctionAndImageDto; -import com.ddang.ddang.auction.infrastructure.persistence.fixture.QuerydslAuctionAndImageRepositoryImplFixture; +import com.ddang.ddang.auction.domain.dto.AuctionAndImageDto; +import com.ddang.ddang.auction.infrastructure.persistence.fixture.QuerydslAuctionAndImageRepositoryFixture; import com.ddang.ddang.configuration.JpaConfiguration; import com.ddang.ddang.configuration.QuerydslConfiguration; +import com.querydsl.jpa.impl.JPAQueryFactory; import java.util.Optional; import org.assertj.core.api.SoftAssertions; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayNameGeneration; import org.junit.jupiter.api.DisplayNameGenerator; import org.junit.jupiter.api.Test; @@ -17,15 +19,19 @@ @Import({JpaConfiguration.class, QuerydslConfiguration.class}) @DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) @SuppressWarnings("NonAsciiCharacters") -class QuerydslAuctionAndImageRepositoryImplTest extends QuerydslAuctionAndImageRepositoryImplFixture { +class QuerydslAuctionAndImageRepositoryTest extends QuerydslAuctionAndImageRepositoryFixture { - @Autowired - JpaAuctionRepository auctionRepository; + QuerydslAuctionAndImageRepository querydslAuctionAndImageRepository; + + @BeforeEach + void setUp(@Autowired final JPAQueryFactory jpaQueryFactory) { + querydslAuctionAndImageRepository = new QuerydslAuctionAndImageRepository(jpaQueryFactory); + } @Test void 경매와_경매_대표이미지를_조회한다() { // when - final Optional actual = auctionRepository.findDtoByAuctionId(경매.getId()); + final Optional actual = querydslAuctionAndImageRepository.findDtoByAuctionId(경매.getId()); // then SoftAssertions.assertSoftly(softAssertions -> { diff --git a/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/QuerydslAuctionRepositoryImplForObjectTest.java b/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/QuerydslAuctionRepositoryImplForObjectTest.java deleted file mode 100644 index f7d2ed39f..000000000 --- a/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/QuerydslAuctionRepositoryImplForObjectTest.java +++ /dev/null @@ -1,78 +0,0 @@ -package com.ddang.ddang.auction.infrastructure.persistence; - -import static org.assertj.core.api.Assertions.assertThat; - -import com.ddang.ddang.auction.domain.Auction; -import com.ddang.ddang.auction.infrastructure.persistence.fixture.QuerydslAuctionRepositoryImplForObjectFixture; -import com.ddang.ddang.category.domain.Category; -import com.ddang.ddang.configuration.JpaConfiguration; -import com.ddang.ddang.configuration.QuerydslConfiguration; -import com.ddang.ddang.region.domain.Region; -import com.ddang.ddang.user.domain.User; -import com.querydsl.jpa.impl.JPAQueryFactory; -import java.util.Optional; -import org.assertj.core.api.SoftAssertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayNameGeneration; -import org.junit.jupiter.api.DisplayNameGenerator; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; -import org.springframework.context.annotation.Import; - -@DataJpaTest -@Import({JpaConfiguration.class, QuerydslConfiguration.class}) -@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) -@SuppressWarnings("NonAsciiCharacters") -class QuerydslAuctionRepositoryImplForObjectTest extends QuerydslAuctionRepositoryImplForObjectFixture { - - QuerydslAuctionRepository querydslAuctionRepository; - - @BeforeEach - void setUp(@Autowired final JPAQueryFactory queryFactory) { - querydslAuctionRepository = new QuerydslAuctionRepositoryImpl(queryFactory); - } - - @Test - void 지정한_아이디에_대한_경매를_조회한다() { - // when - final Optional actual = querydslAuctionRepository.findAuctionById(경매.getId()); - - // then - SoftAssertions.assertSoftly(softAssertions -> { - softAssertions.assertThat(actual).isPresent(); - - final Auction actualAuction = actual.get(); - softAssertions.assertThat(actualAuction.getTitle()).isEqualTo(경매.getTitle()); - softAssertions.assertThat(actualAuction.getId()).isEqualTo(경매.getId()); - softAssertions.assertThat(actualAuction.getAuctionRegions()).hasSize(1); - - final Region actualThirdRegion = actualAuction.getAuctionRegions().get(0).getThirdRegion(); - softAssertions.assertThat(actualThirdRegion.getName()).isEqualTo(개포1동.getName()); - - final Region actualSecondRegion = actualThirdRegion.getSecondRegion(); - softAssertions.assertThat(actualSecondRegion.getName()).isEqualTo(강남구.getName()); - - final Region actualFirstRegion = actualSecondRegion.getFirstRegion(); - softAssertions.assertThat(actualFirstRegion.getName()).isEqualTo(서울특별시.getName()); - - final Category actualSubCategory = actual.get().getSubCategory(); - softAssertions.assertThat(actualSubCategory).isEqualTo(가구_서브_의자_카테고리); - - final Category mainCategory = actualSubCategory.getMainCategory(); - softAssertions.assertThat(mainCategory).isEqualTo(가구_카테고리); - - final User actualSeller = actual.get().getSeller(); - softAssertions.assertThat(actualSeller).isEqualTo(판매자); - }); - } - - @Test - void 지정한_아이디에_해당하는_경매가_없는_경우_빈_Optional을_조회한다() { - // when - final Optional actual = querydslAuctionRepository.findAuctionById(존재하지_않는_경매); - - // then - assertThat(actual).isEmpty(); - } -} diff --git a/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/fixture/AuctionForListSearchByTitleAndSortByIdFixture.java b/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/fixture/AuctionForListSearchByTitleAndSortByIdFixture.java deleted file mode 100644 index d580201a9..000000000 --- a/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/fixture/AuctionForListSearchByTitleAndSortByIdFixture.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.ddang.ddang.auction.infrastructure.persistence.fixture; - -import com.ddang.ddang.auction.domain.Auction; -import com.ddang.ddang.auction.presentation.dto.request.ReadAuctionSearchCondition; -import org.junit.jupiter.api.BeforeEach; -import org.springframework.data.domain.Sort; -import org.springframework.data.domain.Sort.Order; - -@SuppressWarnings("NonAsciiCharacters") -public class AuctionForListSearchByTitleAndSortByIdFixture extends QuerydslAuctionRepositoryImplForListFixture { - - protected Sort id순_정렬 = Sort.by(Order.asc("id")); - protected ReadAuctionSearchCondition 검색어_맥북 = new ReadAuctionSearchCondition("맥북"); - - protected Auction 첫번째_페이지_인덱스_0_id_16; - protected Auction 첫번째_페이지_인덱스_1_id_15; - protected Auction 첫번째_페이지_인덱스_2_id_14; - protected Auction 두번째_페이지_인덱스_0_id_13; - protected Auction 두번째_페이지_인덱스_1_id_12; - protected Auction 두번째_페이지_인덱스_2_id_11; - protected Auction 세번째_페이지_인덱스_0_id_10; - protected Auction 세번째_페이지_인덱스_1_id_9; - protected Auction 세번째_페이지_인덱스_2_id_8; - protected Auction 네번째_페이지_인덱스_0_id_7; - protected Auction 네번째_페이지_인덱스_1_id_4; - protected Auction 네번째_페이지_인덱스_2_id_3; - protected Auction 다섯번째_페이지_인덱스_0_id_2; - protected Auction 다섯번째_페이지_인덱스_1_id_1; - - @BeforeEach - void fixtureSetUp() { - 첫번째_페이지_인덱스_0_id_16 = 경매16; - 첫번째_페이지_인덱스_1_id_15 = 경매15; - 첫번째_페이지_인덱스_2_id_14 = 경매14; - 두번째_페이지_인덱스_0_id_13 = 경매13; - 두번째_페이지_인덱스_1_id_12 = 경매12; - 두번째_페이지_인덱스_2_id_11 = 경매11; - 세번째_페이지_인덱스_0_id_10 = 경매10; - 세번째_페이지_인덱스_1_id_9 = 경매9; - 세번째_페이지_인덱스_2_id_8 = 경매8; - 네번째_페이지_인덱스_0_id_7 = 경매7; - 네번째_페이지_인덱스_1_id_4 = 경매4; - 네번째_페이지_인덱스_2_id_3 = 경매3; - 다섯번째_페이지_인덱스_0_id_2 = 경매2; - 다섯번째_페이지_인덱스_1_id_1 = 경매1; - } -} diff --git a/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/fixture/AuctionForListSearchByTitleFixture.java b/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/fixture/AuctionForListSearchByTitleFixture.java deleted file mode 100644 index 2c7928f3c..000000000 --- a/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/fixture/AuctionForListSearchByTitleFixture.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.ddang.ddang.auction.infrastructure.persistence.fixture; - -import com.ddang.ddang.auction.domain.Auction; -import com.ddang.ddang.auction.presentation.dto.request.ReadAuctionSearchCondition; -import org.junit.jupiter.api.BeforeEach; - -@SuppressWarnings("NonAsciiCharacters") -public class AuctionForListSearchByTitleFixture extends QuerydslAuctionRepositoryImplForListFixture { - - protected ReadAuctionSearchCondition 검색어_맥북 = new ReadAuctionSearchCondition("맥북"); - - protected Auction 첫번째_페이지_인덱스_0_맥북_검색_id_16; - protected Auction 첫번째_페이지_인덱스_1_맥북_검색_id_15; - protected Auction 첫번째_페이지_인덱스_2_맥북_검색_id_14; - protected Auction 두번째_페이지_인덱스_0_맥북_검색_id_13; - protected Auction 두번째_페이지_인덱스_1_맥북_검색_id_12; - protected Auction 두번째_페이지_인덱스_2_맥북_검색_id_11; - protected Auction 세번째_페이지_인덱스_0_맥북_검색_id_10; - protected Auction 세번째_페이지_인덱스_1_맥북_검색_id_9; - protected Auction 세번째_페이지_인덱스_2_맥북_검색_id_8; - protected Auction 네번째_페이지_인덱스_0_맥북_검색_id_7; - protected Auction 네번째_페이지_인덱스_1_맥북_검색_id_4; - protected Auction 네번째_페이지_인덱스_2_맥북_검색_id_3; - protected Auction 다섯번째_페이지_인덱스_0_맥북_검색_id_2; - protected Auction 다섯번째_페이지_인덱스_1_맥북_검색_id_1; - - protected ReadAuctionSearchCondition 검색어_캐비어 = new ReadAuctionSearchCondition("캐비어"); - - @BeforeEach - void fixtureSetUp() { - 첫번째_페이지_인덱스_0_맥북_검색_id_16 = 경매16; - 첫번째_페이지_인덱스_1_맥북_검색_id_15 = 경매15; - 첫번째_페이지_인덱스_2_맥북_검색_id_14 = 경매14; - 두번째_페이지_인덱스_0_맥북_검색_id_13 = 경매13; - 두번째_페이지_인덱스_1_맥북_검색_id_12 = 경매12; - 두번째_페이지_인덱스_2_맥북_검색_id_11 = 경매11; - 세번째_페이지_인덱스_0_맥북_검색_id_10 = 경매10; - 세번째_페이지_인덱스_1_맥북_검색_id_9 = 경매9; - 세번째_페이지_인덱스_2_맥북_검색_id_8 = 경매8; - 네번째_페이지_인덱스_0_맥북_검색_id_7 = 경매7; - 네번째_페이지_인덱스_1_맥북_검색_id_4 = 경매4; - 네번째_페이지_인덱스_2_맥북_검색_id_3 = 경매3; - 다섯번째_페이지_인덱스_0_맥북_검색_id_2 = 경매2; - 다섯번째_페이지_인덱스_1_맥북_검색_id_1 = 경매1; - } -} diff --git a/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/fixture/AuctionForListSortByIdFixture.java b/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/fixture/AuctionForListSortByIdFixture.java deleted file mode 100644 index bfae846c0..000000000 --- a/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/fixture/AuctionForListSortByIdFixture.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.ddang.ddang.auction.infrastructure.persistence.fixture; - -import com.ddang.ddang.auction.domain.Auction; -import com.ddang.ddang.auction.presentation.dto.request.ReadAuctionSearchCondition; -import org.junit.jupiter.api.BeforeEach; -import org.springframework.data.domain.Sort; -import org.springframework.data.domain.Sort.Order; - -@SuppressWarnings("NonAsciiCharacters") -public class AuctionForListSortByIdFixture extends QuerydslAuctionRepositoryImplForListFixture { - - protected Sort id순_정렬 = Sort.by(Order.asc("id")); - protected ReadAuctionSearchCondition 검색어_없음 = new ReadAuctionSearchCondition(null); - - protected Auction 첫번째_페이지_인덱스_0_id_16; - protected Auction 첫번째_페이지_인덱스_1_id_15; - protected Auction 첫번째_페이지_인덱스_2_id_14; - protected Auction 두번째_페이지_인덱스_0_id_13; - protected Auction 두번째_페이지_인덱스_1_id_12; - protected Auction 두번째_페이지_인덱스_2_id_11; - protected Auction 세번째_페이지_인덱스_0_id_10; - protected Auction 세번째_페이지_인덱스_1_id_9; - protected Auction 세번째_페이지_인덱스_2_id_8; - protected Auction 네번째_페이지_인덱스_0_id_7; - protected Auction 네번째_페이지_인덱스_1_id_6; - protected Auction 네번째_페이지_인덱스_2_id_5; - protected Auction 다섯번째_페이지_인덱스_0_id_4; - protected Auction 다섯번째_페이지_인덱스_1_id_3; - protected Auction 다섯번째_페이지_인덱스_2_id_2; - protected Auction 여섯번째_페이지_인덱스_0_id_1; - - @BeforeEach - void fixtureSetUp() { - 첫번째_페이지_인덱스_0_id_16 = 경매16; - 첫번째_페이지_인덱스_1_id_15 = 경매15; - 첫번째_페이지_인덱스_2_id_14 = 경매14; - 두번째_페이지_인덱스_0_id_13 = 경매13; - 두번째_페이지_인덱스_1_id_12 = 경매12; - 두번째_페이지_인덱스_2_id_11 = 경매11; - 세번째_페이지_인덱스_0_id_10 = 경매10; - 세번째_페이지_인덱스_1_id_9 = 경매9; - 세번째_페이지_인덱스_2_id_8 = 경매8; - 네번째_페이지_인덱스_0_id_7 = 경매7; - 네번째_페이지_인덱스_1_id_6 = 경매6; - 네번째_페이지_인덱스_2_id_5 = 경매5; - 다섯번째_페이지_인덱스_0_id_4 = 경매4; - 다섯번째_페이지_인덱스_1_id_3 = 경매3; - 다섯번째_페이지_인덱스_2_id_2 = 경매2; - 여섯번째_페이지_인덱스_0_id_1 = 경매1; - } -} diff --git a/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/fixture/AuctionRepositoryImplFixture.java b/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/fixture/AuctionRepositoryImplFixture.java new file mode 100644 index 000000000..1709f9a71 --- /dev/null +++ b/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/fixture/AuctionRepositoryImplFixture.java @@ -0,0 +1,195 @@ +package com.ddang.ddang.auction.infrastructure.persistence.fixture; + +import com.ddang.ddang.auction.domain.Auction; +import com.ddang.ddang.auction.domain.BidUnit; +import com.ddang.ddang.auction.domain.Price; +import com.ddang.ddang.auction.domain.repository.AuctionRepository; +import com.ddang.ddang.auction.infrastructure.persistence.AuctionRepositoryImpl; +import com.ddang.ddang.auction.infrastructure.persistence.JpaAuctionRepository; +import com.ddang.ddang.auction.infrastructure.persistence.QuerydslAuctionRepository; +import com.ddang.ddang.auction.presentation.dto.request.ReadAuctionSearchCondition; +import com.ddang.ddang.bid.domain.Bid; +import com.ddang.ddang.bid.domain.BidPrice; +import com.ddang.ddang.bid.domain.repository.BidRepository; +import com.ddang.ddang.bid.infrastructure.persistence.BidRepositoryImpl; +import com.ddang.ddang.bid.infrastructure.persistence.JpaBidRepository; +import com.ddang.ddang.category.domain.Category; +import com.ddang.ddang.category.infrastructure.persistence.JpaCategoryRepository; +import com.ddang.ddang.image.domain.ProfileImage; +import com.ddang.ddang.region.domain.AuctionRegion; +import com.ddang.ddang.region.domain.Region; +import com.ddang.ddang.region.domain.repository.RegionRepository; +import com.ddang.ddang.region.infrastructure.persistence.JpaRegionRepository; +import com.ddang.ddang.region.infrastructure.persistence.RegionRepositoryImpl; +import com.ddang.ddang.user.domain.Reliability; +import com.ddang.ddang.user.domain.User; +import com.ddang.ddang.user.domain.repository.UserRepository; +import com.ddang.ddang.user.infrastructure.persistence.JpaUserRepository; +import com.ddang.ddang.user.infrastructure.persistence.UserRepositoryImpl; +import com.querydsl.jpa.impl.JPAQueryFactory; +import org.junit.jupiter.api.BeforeEach; +import org.springframework.beans.factory.annotation.Autowired; + +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.util.List; + +@SuppressWarnings("NonAsciiCharacters") +public class AuctionRepositoryImplFixture { + + @Autowired + private JpaCategoryRepository categoryRepository; + + private AuctionRepository auctionRepository; + + private UserRepository userRepository; + + private RegionRepository regionRepository; + + private BidRepository bidRepository; + + private Instant 시간 = Instant.parse("2023-07-08T22:21:20Z"); + private ZoneId 위치 = ZoneId.of("UTC"); + + protected Long 존재하지_않는_경매_id = -999L; + protected ReadAuctionSearchCondition 검색어_없음 = new ReadAuctionSearchCondition(null); + protected int 페이지_1 = 0; + protected int 페이지_크기 = 3; + protected Auction 저장하기_전_경매_엔티티 = Auction.builder() + .title("제목") + .description("내용") + .bidUnit(new BidUnit(1_000)) + .startPrice(new Price(1_000)) + .closingTime(LocalDateTime.now()) + .build(); + protected Auction 저장된_경매_엔티티; + protected Auction 삭제된_경매_엔티티; + protected Auction 삭제된_경매만_있는_사용자의_삭제된_경매_엔티티; + protected User 판매자; + protected User 삭제한_경매를_갖고_있는_판매자; + protected User 구매자; + protected User 삭제된_경매의_마지막_입찰자; + protected Bid 입찰; + + @BeforeEach + void fixtureSetUp( + @Autowired final JPAQueryFactory jpaQueryFactory, + @Autowired final JpaAuctionRepository jpaAuctionRepository, + @Autowired final JpaUserRepository jpaUserRepository, + @Autowired final JpaRegionRepository jpaRegionRepository, + @Autowired final JpaBidRepository jpaBidRepository + ) { + auctionRepository = new AuctionRepositoryImpl(jpaAuctionRepository, new QuerydslAuctionRepository(jpaQueryFactory)); + userRepository = new UserRepositoryImpl(jpaUserRepository); + regionRepository = new RegionRepositoryImpl(jpaRegionRepository); + bidRepository = new BidRepositoryImpl(jpaBidRepository); + + final Region 서울특별시 = new Region("서울특별시"); + final Region 강남구 = new Region("강남구"); + final Region 역삼동 = new Region("역삼동"); + + 서울특별시.addSecondRegion(강남구); + 강남구.addThirdRegion(역삼동); + + regionRepository.save(서울특별시); + + final Category 가구_카테고리 = new Category("가구"); + final Category 가구_서브_의자_카테고리 = new Category("의자"); + + 가구_카테고리.addSubCategory(가구_서브_의자_카테고리); + + categoryRepository.save(가구_카테고리); + + 판매자 = User.builder() + .name("판매자") + .profileImage(new ProfileImage("upload.png", "store.png")) + .reliability(new Reliability(4.7d)) + .oauthId("12345") + .build(); + 삭제한_경매를_갖고_있는_판매자 = User.builder() + .name("판매자2") + .profileImage(new ProfileImage("upload.png", "store.png")) + .reliability(new Reliability(4.7d)) + .oauthId("12346") + .build(); + + 구매자 = User.builder() + .name("구매자") + .profileImage(new ProfileImage("upload.png", "store.png")) + .reliability(new Reliability(2.7d)) + .oauthId("54321") + .build(); + 삭제된_경매의_마지막_입찰자 = User.builder() + .name("삭제된 경매의 마지막 입찰자") + .profileImage(new ProfileImage("upload.png", "store.png")) + .reliability(new Reliability(2.7d)) + .oauthId("54322") + .build(); + + userRepository.save(판매자); + userRepository.save(삭제한_경매를_갖고_있는_판매자); + userRepository.save(구매자); + userRepository.save(삭제된_경매의_마지막_입찰자); + + 저장된_경매_엔티티 = Auction.builder() + .title("경매 상품 1") + .description("이것은 경매 상품 1 입니다.") + .bidUnit(new BidUnit(1_000)) + .startPrice(new Price(1_000)) + .closingTime(시간.atZone(위치).toLocalDateTime()) + .subCategory(가구_서브_의자_카테고리) + .seller(판매자) + .build(); + 삭제된_경매_엔티티 = Auction.builder() + .title("경매 상품 1") + .description("이것은 경매 상품 1 입니다.") + .bidUnit(new BidUnit(1_000)) + .startPrice(new Price(1_000)) + .closingTime(시간.atZone(위치).toLocalDateTime()) + .subCategory(가구_서브_의자_카테고리) + .seller(판매자) + .build(); + 삭제된_경매만_있는_사용자의_삭제된_경매_엔티티 = Auction.builder() + .title("경매 상품 1") + .description("이것은 경매 상품 1 입니다.") + .bidUnit(new BidUnit(1_000)) + .startPrice(new Price(1_000)) + .closingTime(시간.atZone(위치).toLocalDateTime()) + .subCategory(가구_서브_의자_카테고리) + .seller(삭제한_경매를_갖고_있는_판매자) + .build(); + + 삭제된_경매_엔티티.addAuctionRegions(List.of(new AuctionRegion(역삼동))); + 저장된_경매_엔티티.addAuctionRegions(List.of(new AuctionRegion(역삼동))); + 삭제된_경매_엔티티.delete(); + 삭제된_경매만_있는_사용자의_삭제된_경매_엔티티.addAuctionRegions(List.of(new AuctionRegion(역삼동))); + 삭제된_경매만_있는_사용자의_삭제된_경매_엔티티.delete(); + bidding(저장된_경매_엔티티, 구매자); + addAuctioneerCount(저장된_경매_엔티티, 구매자, 1); + bidding(삭제된_경매_엔티티, 삭제된_경매의_마지막_입찰자); + addAuctioneerCount(삭제된_경매_엔티티, 삭제된_경매의_마지막_입찰자, 1); + + auctionRepository.save(삭제된_경매_엔티티); + auctionRepository.save(저장된_경매_엔티티); + auctionRepository.save(삭제된_경매만_있는_사용자의_삭제된_경매_엔티티); + } + + private void bidding(final Auction targetAuction, final User bidder) { + final Bid lastBid = new Bid(targetAuction, bidder, new BidPrice(1)); + + bidRepository.save(lastBid); + + targetAuction.updateLastBid(lastBid); + } + + private void addAuctioneerCount(final Auction targetAuction, final User bidder, final int count) { + final Bid lastBid = new Bid(targetAuction, bidder, new BidPrice(1)); + + bidRepository.save(lastBid); + + for (int i = 0; i < count; i++) { + targetAuction.updateLastBid(lastBid); + } + } +} diff --git a/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/fixture/JpaAuctionRepositoryFixture.java b/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/fixture/JpaAuctionRepositoryFixture.java index 7e09374d8..2eb4f60ac 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/fixture/JpaAuctionRepositoryFixture.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/fixture/JpaAuctionRepositoryFixture.java @@ -4,8 +4,21 @@ import com.ddang.ddang.auction.domain.BidUnit; import com.ddang.ddang.auction.domain.Price; import com.ddang.ddang.auction.infrastructure.persistence.JpaAuctionRepository; +import com.ddang.ddang.bid.domain.Bid; +import com.ddang.ddang.bid.domain.BidPrice; +import com.ddang.ddang.bid.infrastructure.persistence.JpaBidRepository; +import com.ddang.ddang.category.domain.Category; +import com.ddang.ddang.category.infrastructure.persistence.JpaCategoryRepository; +import com.ddang.ddang.image.domain.ProfileImage; +import com.ddang.ddang.region.domain.AuctionRegion; +import com.ddang.ddang.region.domain.Region; +import com.ddang.ddang.region.infrastructure.persistence.JpaRegionRepository; +import com.ddang.ddang.user.domain.Reliability; +import com.ddang.ddang.user.domain.User; +import com.ddang.ddang.user.infrastructure.persistence.JpaUserRepository; import jakarta.persistence.EntityManager; import jakarta.persistence.PersistenceContext; + import java.time.Instant; import java.time.LocalDateTime; import java.time.ZoneId; @@ -20,39 +33,128 @@ public class JpaAuctionRepositoryFixture { @PersistenceContext private EntityManager em; + @Autowired + private JpaUserRepository userRepository; + @Autowired private JpaAuctionRepository auctionRepository; + @Autowired + private JpaRegionRepository regionRepository; + + @Autowired + private JpaCategoryRepository categoryRepository; + + @Autowired + private JpaBidRepository bidRepository; + private Instant 시간 = Instant.parse("2023-07-08T22:21:20Z"); private ZoneId 위치 = ZoneId.of("UTC"); + protected Long 존재하지_않는_경매_id = -999L; + protected User 판매자; + protected User 입찰자; + protected User 삭제한_경매를_갖고_있는_판매자; + protected User 삭제된_경매의_마지막_입찰자; protected Auction 저장하기_전_경매_엔티티 = Auction.builder() - .title("제목") - .description("내용") - .bidUnit(new BidUnit(1_000)) - .startPrice(new Price(1_000)) - .closingTime(LocalDateTime.now()) - .build(); - protected Auction 저장된_경매_엔티티 = Auction.builder() - .title("경매 상품 1") - .description("이것은 경매 상품 1 입니다.") - .bidUnit(new BidUnit(1_000)) - .startPrice(new Price(1_000)) - .closingTime(시간.atZone(위치).toLocalDateTime()) - .build(); - protected Auction 삭제된_경매_엔티티 = Auction.builder() - .title("경매 상품 1") - .description("이것은 경매 상품 1 입니다.") - .bidUnit(new BidUnit(1_000)) - .startPrice(new Price(1_000)) - .closingTime(시간.atZone(위치).toLocalDateTime()) - .build(); + .title("제목") + .description("내용") + .bidUnit(new BidUnit(1_000)) + .startPrice(new Price(1_000)) + .closingTime(LocalDateTime.now()) + .build(); + protected Auction 저장된_경매_엔티티; + protected Auction 삭제된_경매_엔티티; + protected Auction 삭제된_경매만_있는_사용자의_삭제된_경매_엔티티; + protected Bid 입찰; @BeforeEach void setUp() { + final Region 서울특별시 = new Region("서울특별시"); + final Region 강남구 = new Region("강남구"); + final Region 역삼동 = new Region("역삼동"); + + 서울특별시.addSecondRegion(강남구); + 강남구.addThirdRegion(역삼동); + + regionRepository.save(서울특별시); + + final Category 가구_카테고리 = new Category("가구"); + final Category 가구_서브_의자_카테고리 = new Category("의자"); + + 가구_카테고리.addSubCategory(가구_서브_의자_카테고리); + + categoryRepository.save(가구_카테고리); + + 판매자 = User.builder() + .name("판매자") + .profileImage(new ProfileImage("upload.png", "store.png")) + .reliability(new Reliability(4.7d)) + .oauthId("12345") + .build(); + 입찰자 = User.builder() + .name("입찰자") + .profileImage(new ProfileImage("upload.png", "store.png")) + .reliability(new Reliability(4.7d)) + .oauthId("12346") + .build(); + 삭제한_경매를_갖고_있는_판매자 = User.builder() + .name("판매자2") + .profileImage(new ProfileImage("upload.png", "store.png")) + .reliability(new Reliability(4.7d)) + .oauthId("12347") + .build(); + 삭제된_경매의_마지막_입찰자 = User.builder() + .name("삭제된 경매의 마지막 입찰자") + .profileImage(new ProfileImage("upload.png", "store.png")) + .reliability(new Reliability(2.7d)) + .oauthId("12348") + .build(); + + userRepository.saveAll(List.of(판매자, 입찰자, 삭제한_경매를_갖고_있는_판매자, 삭제된_경매의_마지막_입찰자)); + + 저장된_경매_엔티티 = Auction.builder() + .seller(판매자) + .title("경매 상품 1") + .description("이것은 경매 상품 1 입니다.") + .bidUnit(new BidUnit(1_000)) + .startPrice(new Price(1_000)) + .closingTime(시간.atZone(위치).toLocalDateTime()) + .subCategory(가구_서브_의자_카테고리) + .build(); + 삭제된_경매_엔티티 = Auction.builder() + .seller(판매자) + .title("경매 상품 1") + .description("이것은 경매 상품 1 입니다.") + .bidUnit(new BidUnit(1_000)) + .startPrice(new Price(1_000)) + .closingTime(시간.atZone(위치).toLocalDateTime()) + .subCategory(가구_서브_의자_카테고리) + .build(); + 삭제된_경매만_있는_사용자의_삭제된_경매_엔티티 = Auction.builder() + .title("경매 상품 1") + .description("이것은 경매 상품 1 입니다.") + .bidUnit(new BidUnit(1_000)) + .startPrice(new Price(1_000)) + .closingTime(시간.atZone(위치).toLocalDateTime()) + .subCategory(가구_서브_의자_카테고리) + .seller(삭제한_경매를_갖고_있는_판매자) + .build(); + + 저장된_경매_엔티티.addAuctionRegions(List.of(new AuctionRegion(역삼동))); + 삭제된_경매_엔티티.addAuctionRegions(List.of(new AuctionRegion(역삼동))); 삭제된_경매_엔티티.delete(); + 삭제된_경매만_있는_사용자의_삭제된_경매_엔티티.addAuctionRegions(List.of(new AuctionRegion(역삼동))); + 삭제된_경매만_있는_사용자의_삭제된_경매_엔티티.delete(); + + auctionRepository.saveAll(List.of(저장된_경매_엔티티, 삭제된_경매_엔티티, 삭제된_경매만_있는_사용자의_삭제된_경매_엔티티)); + + final Bid 입찰 = new Bid(저장된_경매_엔티티, 입찰자, new BidPrice(10_000)); + 저장된_경매_엔티티.updateLastBid(입찰); + final Bid 삭제된_경매_입찰 = new Bid(삭제된_경매_엔티티, 삭제된_경매의_마지막_입찰자, new BidPrice(10_000)); + 삭제된_경매_엔티티.updateLastBid(삭제된_경매_입찰); - auctionRepository.saveAll(List.of(저장된_경매_엔티티, 삭제된_경매_엔티티)); + bidRepository.saveAll(List.of(입찰, 삭제된_경매_입찰)); em.flush(); em.clear(); diff --git a/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/fixture/QuerydslAuctionAndImageRepositoryImplFixture.java b/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/fixture/QuerydslAuctionAndImageRepositoryFixture.java similarity index 67% rename from backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/fixture/QuerydslAuctionAndImageRepositoryImplFixture.java rename to backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/fixture/QuerydslAuctionAndImageRepositoryFixture.java index 485cbfeaa..949634098 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/fixture/QuerydslAuctionAndImageRepositoryImplFixture.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/fixture/QuerydslAuctionAndImageRepositoryFixture.java @@ -3,13 +3,19 @@ import com.ddang.ddang.auction.domain.Auction; import com.ddang.ddang.auction.domain.BidUnit; import com.ddang.ddang.auction.domain.Price; +import com.ddang.ddang.auction.domain.repository.AuctionRepository; +import com.ddang.ddang.auction.infrastructure.persistence.AuctionRepositoryImpl; import com.ddang.ddang.auction.infrastructure.persistence.JpaAuctionRepository; +import com.ddang.ddang.auction.infrastructure.persistence.QuerydslAuctionRepository; import com.ddang.ddang.image.domain.AuctionImage; import com.ddang.ddang.image.domain.ProfileImage; import com.ddang.ddang.image.infrastructure.persistence.JpaAuctionImageRepository; import com.ddang.ddang.user.domain.Reliability; import com.ddang.ddang.user.domain.User; +import com.ddang.ddang.user.domain.repository.UserRepository; import com.ddang.ddang.user.infrastructure.persistence.JpaUserRepository; +import com.ddang.ddang.user.infrastructure.persistence.UserRepositoryImpl; +import com.querydsl.jpa.impl.JPAQueryFactory; import jakarta.persistence.EntityManager; import jakarta.persistence.PersistenceContext; import org.junit.jupiter.api.BeforeEach; @@ -19,26 +25,34 @@ import java.util.List; @SuppressWarnings("NonAsciiCharacters") -public class QuerydslAuctionAndImageRepositoryImplFixture { +public class QuerydslAuctionAndImageRepositoryFixture { @PersistenceContext private EntityManager em; - @Autowired - private JpaAuctionRepository auctionRepository; - @Autowired private JpaAuctionImageRepository auctionImageRepository; + + private UserRepository userRepository; - @Autowired - private JpaUserRepository userRepository; + private AuctionRepository auctionRepository; private User 사용자; protected Auction 경매; protected AuctionImage 경매_이미지; @BeforeEach - void setUp() { + void fixtureSetUp( + @Autowired JPAQueryFactory jpaQueryFactory, + @Autowired JpaAuctionRepository jpaAuctionRepository, + @Autowired JpaUserRepository jpaUserRepository + ) { + auctionRepository = new AuctionRepositoryImpl( + jpaAuctionRepository, + new QuerydslAuctionRepository(jpaQueryFactory) + ); + userRepository = new UserRepositoryImpl(jpaUserRepository); + 경매 = Auction.builder() .title("경매 상품 1") .description("이것은 경매 상품 1 입니다.") diff --git a/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/fixture/QuerydslAuctionRepositoryImplForListFixture.java b/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/fixture/QuerydslAuctionRepositoryForListFixture.java similarity index 82% rename from backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/fixture/QuerydslAuctionRepositoryImplForListFixture.java rename to backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/fixture/QuerydslAuctionRepositoryForListFixture.java index b293a72ed..9e041f173 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/fixture/QuerydslAuctionRepositoryImplForListFixture.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/fixture/QuerydslAuctionRepositoryForListFixture.java @@ -3,7 +3,10 @@ import com.ddang.ddang.auction.domain.Auction; import com.ddang.ddang.auction.domain.BidUnit; import com.ddang.ddang.auction.domain.Price; +import com.ddang.ddang.auction.domain.repository.AuctionRepository; +import com.ddang.ddang.auction.infrastructure.persistence.AuctionRepositoryImpl; import com.ddang.ddang.auction.infrastructure.persistence.JpaAuctionRepository; +import com.ddang.ddang.auction.infrastructure.persistence.QuerydslAuctionRepository; import com.ddang.ddang.bid.domain.Bid; import com.ddang.ddang.bid.domain.BidPrice; import com.ddang.ddang.bid.infrastructure.persistence.JpaBidRepository; @@ -13,6 +16,7 @@ import com.ddang.ddang.user.domain.Reliability; import com.ddang.ddang.user.domain.User; import com.ddang.ddang.user.infrastructure.persistence.JpaUserRepository; +import com.querydsl.jpa.impl.JPAQueryFactory; import jakarta.persistence.EntityManager; import jakarta.persistence.PersistenceContext; import org.junit.jupiter.api.BeforeEach; @@ -22,16 +26,19 @@ import java.util.List; @SuppressWarnings("NonAsciiCharacters") -public class QuerydslAuctionRepositoryImplForListFixture { +public class QuerydslAuctionRepositoryForListFixture { @PersistenceContext private EntityManager em; @Autowired - private JpaUserRepository userRepository; + private JPAQueryFactory queryFactory; @Autowired - private JpaAuctionRepository auctionRepository; + private JpaAuctionRepository jpaAuctionRepository; + + @Autowired + private JpaUserRepository userRepository; @Autowired private JpaCategoryRepository categoryRepository; @@ -129,6 +136,7 @@ void commonFixtureSetUp() { .startPrice(new Price(1_000)) .closingTime(현재시간.minusDays(2)) .seller(판매자_0_3점_1) + .subCategory(기타_서브_기타_카테고리) .build(); bidding(경매6, 판매자_4_7점); addAuctioneerCount(경매6, 7); @@ -138,7 +146,8 @@ void commonFixtureSetUp() { .bidUnit(new BidUnit(1_000)) .startPrice(new Price(1_000)) .closingTime(현재시간.minusDays(4)) - .seller(판매자_3_5점) // ㅇㅋ + .seller(판매자_3_5점) + .subCategory(기타_서브_기타_카테고리) .build(); bidding(경매8, 판매자_4_7점); addAuctioneerCount(경매8, 5); @@ -148,7 +157,8 @@ void commonFixtureSetUp() { .bidUnit(new BidUnit(1_000)) .startPrice(new Price(1_000)) .closingTime(현재시간.minusDays(4)) - .seller(판매자_3_5점) // ㅇㅋ + .seller(판매자_3_5점) + .subCategory(기타_서브_기타_카테고리) .build(); bidding(경매16, 판매자_4_7점); addAuctioneerCount(경매16, 4); @@ -159,6 +169,7 @@ void commonFixtureSetUp() { .startPrice(new Price(1_000)) .closingTime(현재시간.minusDays(4)) .seller(판매자_2_1점) + .subCategory(기타_서브_기타_카테고리) .build(); bidding(경매9, 판매자_4_7점); addAuctioneerCount(경매9, 5); @@ -169,6 +180,7 @@ void commonFixtureSetUp() { .startPrice(new Price(1_000)) .closingTime(현재시간.minusDays(4)) .seller(판매자_2_1점) + .subCategory(기타_서브_기타_카테고리) .build(); bidding(경매13, 판매자_4_7점); addAuctioneerCount(경매13, 5); @@ -179,6 +191,7 @@ void commonFixtureSetUp() { .startPrice(new Price(1_000)) .closingTime(현재시간.minusDays(3)) .seller(판매자_4_7점) + .subCategory(기타_서브_기타_카테고리) .build(); addAuctioneerCount(경매7, 3); 경매1 = Auction.builder() @@ -188,6 +201,7 @@ void commonFixtureSetUp() { .startPrice(new Price(1_000)) .closingTime(현재시간.minusDays(5)) .seller(판매자_4_7점) + .subCategory(기타_서브_기타_카테고리) .build(); addAuctioneerCount(경매1, 2); bidding(경매16, 판매자_4_7점); @@ -198,6 +212,7 @@ void commonFixtureSetUp() { .startPrice(new Price(1_000)) .closingTime(현재시간.plusDays(4)) .seller(판매자_2_1점) + .subCategory(기타_서브_기타_카테고리) .build(); addAuctioneerCount(경매2, 1); 경매3 = Auction.builder() @@ -207,6 +222,7 @@ void commonFixtureSetUp() { .startPrice(new Price(1_000)) .closingTime(현재시간.plusDays(3)) .seller(판매자_2_1점) + .subCategory(기타_서브_기타_카테고리) .build(); addAuctioneerCount(경매3, 4); 경매4 = Auction.builder() @@ -216,6 +232,7 @@ void commonFixtureSetUp() { .startPrice(new Price(1_000)) .closingTime(현재시간.plusDays(2)) .seller(판매자_5_0점) + .subCategory(기타_서브_기타_카테고리) .build(); addAuctioneerCount(경매4, 7); 경매5 = Auction.builder() @@ -225,6 +242,7 @@ void commonFixtureSetUp() { .startPrice(new Price(1_000)) .closingTime(현재시간.plusDays(1)) .seller(판매자_1_5점) + .subCategory(기타_서브_기타_카테고리) .build(); addAuctioneerCount(경매5, 4); 경매10 = Auction.builder() @@ -233,7 +251,8 @@ void commonFixtureSetUp() { .bidUnit(new BidUnit(1_000)) .startPrice(new Price(1_000)) .closingTime(현재시간.plusDays(4)) - .seller(판매자_3_5점) // ㅇㅋ + .seller(판매자_3_5점) + .subCategory(기타_서브_기타_카테고리) .build(); addAuctioneerCount(경매10, 6); 경매11 = Auction.builder() @@ -243,6 +262,7 @@ void commonFixtureSetUp() { .startPrice(new Price(1_000)) .closingTime(현재시간.plusDays(4)) .seller(판매자_2_1점) + .subCategory(기타_서브_기타_카테고리) .build(); addAuctioneerCount(경매11, 6); 경매12 = Auction.builder() @@ -251,7 +271,8 @@ void commonFixtureSetUp() { .bidUnit(new BidUnit(1_000)) .startPrice(new Price(1_000)) .closingTime(현재시간.plusDays(4)) - .seller(판매자_3_5점) // ㅇㅋ + .seller(판매자_3_5점) + .subCategory(기타_서브_기타_카테고리) .build(); addAuctioneerCount(경매12, 6); 경매14 = Auction.builder() @@ -260,7 +281,8 @@ void commonFixtureSetUp() { .bidUnit(new BidUnit(1_000)) .startPrice(new Price(1_000)) .closingTime(현재시간.plusDays(4)) - .seller(판매자_3_5점) // ㅇㅋ + .seller(판매자_3_5점) + .subCategory(기타_서브_기타_카테고리) .build(); addAuctioneerCount(경매14, 6); 경매15 = Auction.builder() @@ -270,28 +292,31 @@ void commonFixtureSetUp() { .startPrice(new Price(1_000)) .closingTime(현재시간.plusDays(4)) .seller(판매자_2_1점) + .subCategory(기타_서브_기타_카테고리) .build(); addAuctioneerCount(경매15, 6); - final List 경매들 = List.of( - 경매1, - 경매2, - 경매3, - 경매4, - 경매5, - 경매6, - 경매7, - 경매8, - 경매9, - 경매10, - 경매11, - 경매12, - 경매13, - 경매14, - 경매15, - 경매16 + final AuctionRepository auctionRepository = new AuctionRepositoryImpl( + jpaAuctionRepository, + new QuerydslAuctionRepository(queryFactory) ); - auctionRepository.saveAll(경매들); + + auctionRepository.save(경매1); + auctionRepository.save(경매2); + auctionRepository.save(경매3); + auctionRepository.save(경매4); + auctionRepository.save(경매5); + auctionRepository.save(경매6); + auctionRepository.save(경매7); + auctionRepository.save(경매8); + auctionRepository.save(경매9); + auctionRepository.save(경매10); + auctionRepository.save(경매11); + auctionRepository.save(경매12); + auctionRepository.save(경매13); + auctionRepository.save(경매14); + auctionRepository.save(경매15); + auctionRepository.save(경매16); em.flush(); em.clear(); diff --git a/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/fixture/QuerydslAuctionRepositoryImplForObjectFixture.java b/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/fixture/QuerydslAuctionRepositoryForObjectFixture.java similarity index 77% rename from backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/fixture/QuerydslAuctionRepositoryImplForObjectFixture.java rename to backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/fixture/QuerydslAuctionRepositoryForObjectFixture.java index 617113574..0869c08c7 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/fixture/QuerydslAuctionRepositoryImplForObjectFixture.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/fixture/QuerydslAuctionRepositoryForObjectFixture.java @@ -3,7 +3,10 @@ import com.ddang.ddang.auction.domain.Auction; import com.ddang.ddang.auction.domain.BidUnit; import com.ddang.ddang.auction.domain.Price; +import com.ddang.ddang.auction.domain.repository.AuctionRepository; +import com.ddang.ddang.auction.infrastructure.persistence.AuctionRepositoryImpl; import com.ddang.ddang.auction.infrastructure.persistence.JpaAuctionRepository; +import com.ddang.ddang.auction.infrastructure.persistence.QuerydslAuctionRepository; import com.ddang.ddang.category.domain.Category; import com.ddang.ddang.category.infrastructure.persistence.JpaCategoryRepository; import com.ddang.ddang.image.domain.ProfileImage; @@ -13,33 +16,35 @@ import com.ddang.ddang.user.domain.Reliability; import com.ddang.ddang.user.domain.User; import com.ddang.ddang.user.infrastructure.persistence.JpaUserRepository; +import com.querydsl.jpa.impl.JPAQueryFactory; import jakarta.persistence.EntityManager; import jakarta.persistence.PersistenceContext; -import org.junit.jupiter.api.BeforeEach; -import org.springframework.beans.factory.annotation.Autowired; - import java.time.LocalDateTime; import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.springframework.beans.factory.annotation.Autowired; @SuppressWarnings("NonAsciiCharacters") -public class QuerydslAuctionRepositoryImplForObjectFixture { +public class QuerydslAuctionRepositoryForObjectFixture { @PersistenceContext - EntityManager em; + private EntityManager em; + + @Autowired + private JPAQueryFactory queryFactory; @Autowired - JpaUserRepository userRepository; + private JpaAuctionRepository jpaAuctionRepository; @Autowired - JpaAuctionRepository auctionRepository; + private JpaUserRepository userRepository; @Autowired - JpaRegionRepository regionRepository; + private JpaRegionRepository regionRepository; @Autowired - JpaCategoryRepository categoryRepository; + private JpaCategoryRepository categoryRepository; - protected Long 존재하지_않는_경매 = -999L; protected Auction 경매; protected Region 서울특별시; protected Region 강남구; @@ -87,6 +92,13 @@ void totalFixtureSetUp() { 경매.addAuctionRegions(List.of(직거래_지역)); + final AuctionRepository auctionRepository = new AuctionRepositoryImpl( + jpaAuctionRepository, + new QuerydslAuctionRepository(queryFactory) + ); auctionRepository.save(경매); + + em.flush(); + em.clear(); } } diff --git a/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/fixture/AuctionForListByBidderIdFixture.java b/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/fixture/list/AuctionForListByBidderIdFixture.java similarity index 90% rename from backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/fixture/AuctionForListByBidderIdFixture.java rename to backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/fixture/list/AuctionForListByBidderIdFixture.java index 4d0b1f940..6cf82c487 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/fixture/AuctionForListByBidderIdFixture.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/fixture/list/AuctionForListByBidderIdFixture.java @@ -1,11 +1,12 @@ -package com.ddang.ddang.auction.infrastructure.persistence.fixture; +package com.ddang.ddang.auction.infrastructure.persistence.fixture.list; import com.ddang.ddang.auction.domain.Auction; +import com.ddang.ddang.auction.infrastructure.persistence.fixture.QuerydslAuctionRepositoryForListFixture; import com.ddang.ddang.user.domain.User; import org.junit.jupiter.api.BeforeEach; @SuppressWarnings("NonAsciiCharacters") -public class AuctionForListByBidderIdFixture extends QuerydslAuctionRepositoryImplForListFixture { +public class AuctionForListByBidderIdFixture extends QuerydslAuctionRepositoryForListFixture { protected User 참여한_경매가_7개인_사용자; protected Auction 첫번째_페이지_인덱스_0; diff --git a/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/fixture/AuctionForListByUserIdFixture.java b/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/fixture/list/AuctionForListByUserIdFixture.java similarity index 88% rename from backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/fixture/AuctionForListByUserIdFixture.java rename to backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/fixture/list/AuctionForListByUserIdFixture.java index 57cef12fa..3db8a307c 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/fixture/AuctionForListByUserIdFixture.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/fixture/list/AuctionForListByUserIdFixture.java @@ -1,11 +1,12 @@ -package com.ddang.ddang.auction.infrastructure.persistence.fixture; +package com.ddang.ddang.auction.infrastructure.persistence.fixture.list; import com.ddang.ddang.auction.domain.Auction; +import com.ddang.ddang.auction.infrastructure.persistence.fixture.QuerydslAuctionRepositoryForListFixture; import com.ddang.ddang.user.domain.User; import org.junit.jupiter.api.BeforeEach; @SuppressWarnings("NonAsciiCharacters") -public class AuctionForListByUserIdFixture extends QuerydslAuctionRepositoryImplForListFixture { +public class AuctionForListByUserIdFixture extends QuerydslAuctionRepositoryForListFixture { protected User 등록한_경매가_5개인_사용자; protected Auction 첫번째_페이지_인덱스_0; diff --git a/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/fixture/AuctionForListSearchByTitleAndSortByAuctioneerCountFixture.java b/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/fixture/list/AuctionForListSearchByTitleAndSortByAuctioneerCountFixture.java similarity index 94% rename from backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/fixture/AuctionForListSearchByTitleAndSortByAuctioneerCountFixture.java rename to backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/fixture/list/AuctionForListSearchByTitleAndSortByAuctioneerCountFixture.java index 8d5a0f148..98982d5f4 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/fixture/AuctionForListSearchByTitleAndSortByAuctioneerCountFixture.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/fixture/list/AuctionForListSearchByTitleAndSortByAuctioneerCountFixture.java @@ -1,13 +1,15 @@ -package com.ddang.ddang.auction.infrastructure.persistence.fixture; +package com.ddang.ddang.auction.infrastructure.persistence.fixture.list; import com.ddang.ddang.auction.domain.Auction; +import com.ddang.ddang.auction.infrastructure.persistence.fixture.QuerydslAuctionRepositoryForListFixture; import com.ddang.ddang.auction.presentation.dto.request.ReadAuctionSearchCondition; import org.junit.jupiter.api.BeforeEach; import org.springframework.data.domain.Sort; import org.springframework.data.domain.Sort.Order; @SuppressWarnings("NonAsciiCharacters") -public class AuctionForListSearchByTitleAndSortByAuctioneerCountFixture extends QuerydslAuctionRepositoryImplForListFixture { +public class AuctionForListSearchByTitleAndSortByAuctioneerCountFixture extends + QuerydslAuctionRepositoryForListFixture { protected Sort 참여_인원순_정렬 = Sort.by(Order.asc("auctioneerCount")); protected ReadAuctionSearchCondition 검색어_맥북 = new ReadAuctionSearchCondition("맥북"); diff --git a/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/fixture/AuctionForListSearchByTitleAndSortByClosingTimeFixture.java b/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/fixture/list/AuctionForListSearchByTitleAndSortByClosingTimeFixture.java similarity index 94% rename from backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/fixture/AuctionForListSearchByTitleAndSortByClosingTimeFixture.java rename to backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/fixture/list/AuctionForListSearchByTitleAndSortByClosingTimeFixture.java index b11603ab9..56df73783 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/fixture/AuctionForListSearchByTitleAndSortByClosingTimeFixture.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/fixture/list/AuctionForListSearchByTitleAndSortByClosingTimeFixture.java @@ -1,13 +1,14 @@ -package com.ddang.ddang.auction.infrastructure.persistence.fixture; +package com.ddang.ddang.auction.infrastructure.persistence.fixture.list; import com.ddang.ddang.auction.domain.Auction; +import com.ddang.ddang.auction.infrastructure.persistence.fixture.QuerydslAuctionRepositoryForListFixture; import com.ddang.ddang.auction.presentation.dto.request.ReadAuctionSearchCondition; import org.junit.jupiter.api.BeforeEach; import org.springframework.data.domain.Sort; import org.springframework.data.domain.Sort.Order; @SuppressWarnings("NonAsciiCharacters") -public class AuctionForListSearchByTitleAndSortByClosingTimeFixture extends QuerydslAuctionRepositoryImplForListFixture { +public class AuctionForListSearchByTitleAndSortByClosingTimeFixture extends QuerydslAuctionRepositoryForListFixture { protected Sort 마감_임박순_정렬 = Sort.by(Order.asc("closingTime")); protected ReadAuctionSearchCondition 검색어_맥북 = new ReadAuctionSearchCondition("맥북"); diff --git a/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/fixture/list/AuctionForListSearchByTitleAndSortByIdFixture.java b/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/fixture/list/AuctionForListSearchByTitleAndSortByIdFixture.java new file mode 100644 index 000000000..6c7050bf6 --- /dev/null +++ b/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/fixture/list/AuctionForListSearchByTitleAndSortByIdFixture.java @@ -0,0 +1,48 @@ +package com.ddang.ddang.auction.infrastructure.persistence.fixture.list; + +import com.ddang.ddang.auction.domain.Auction; +import com.ddang.ddang.auction.infrastructure.persistence.fixture.QuerydslAuctionRepositoryForListFixture; +import com.ddang.ddang.auction.presentation.dto.request.ReadAuctionSearchCondition; +import org.junit.jupiter.api.BeforeEach; +import org.springframework.data.domain.Sort; +import org.springframework.data.domain.Sort.Order; + +@SuppressWarnings("NonAsciiCharacters") +public class AuctionForListSearchByTitleAndSortByIdFixture extends QuerydslAuctionRepositoryForListFixture { + + protected Sort id순_정렬 = Sort.by(Order.asc("id")); + protected ReadAuctionSearchCondition 검색어_맥북 = new ReadAuctionSearchCondition("맥북"); + + protected Auction 첫번째_페이지_인덱스_0_4일_후_마감_id_15; + protected Auction 첫번째_페이지_인덱스_1_4일_후_마감_id_14; + protected Auction 첫번째_페이지_인덱스_2_4일_후_마감_id_12; + protected Auction 두번째_페이지_인덱스_0_4일_후_마감_id_11; + protected Auction 두번째_페이지_인덱스_1_4일_후_마감_id_10; + protected Auction 두번째_페이지_인덱스_2_2일_후_마감_id_4; + protected Auction 세번째_페이지_인덱스_0_3일_후_마감_id_3; + protected Auction 세번째_페이지_인덱스_1_4일_후_마감_id_2; + protected Auction 세번째_페이지_인덱스_2_4일_전_마감_id_16; + protected Auction 네번째_페이지_인덱스_0_4일_전_마감_id_13; + protected Auction 네번째_페이지_인덱스_1_4일_전_마감_id_9; + protected Auction 네번째_페이지_인덱스_2_4일_전_마감_id_8; + protected Auction 다섯번째_페이지_인덱스_0_4일_전_마감_id_7; + protected Auction 다섯번째_페이지_인덱스_1_5일_전_마감_id_1; + + @BeforeEach + void fixtureSetUp() { + 첫번째_페이지_인덱스_0_4일_후_마감_id_15 = 경매15; + 첫번째_페이지_인덱스_1_4일_후_마감_id_14 = 경매14; + 첫번째_페이지_인덱스_2_4일_후_마감_id_12 = 경매12; + 두번째_페이지_인덱스_0_4일_후_마감_id_11 = 경매11; + 두번째_페이지_인덱스_1_4일_후_마감_id_10 = 경매10; + 두번째_페이지_인덱스_2_2일_후_마감_id_4 = 경매4; + 세번째_페이지_인덱스_0_3일_후_마감_id_3 = 경매3; + 세번째_페이지_인덱스_1_4일_후_마감_id_2 = 경매2; + 세번째_페이지_인덱스_2_4일_전_마감_id_16 = 경매16; + 네번째_페이지_인덱스_0_4일_전_마감_id_13 = 경매13; + 네번째_페이지_인덱스_1_4일_전_마감_id_9 = 경매9; + 네번째_페이지_인덱스_2_4일_전_마감_id_8 = 경매8; + 다섯번째_페이지_인덱스_0_4일_전_마감_id_7 = 경매7; + 다섯번째_페이지_인덱스_1_5일_전_마감_id_1 = 경매1; + } +} diff --git a/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/fixture/AuctionForListSearchByTitleAndSortByReliabilityFixture.java b/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/fixture/list/AuctionForListSearchByTitleAndSortByReliabilityFixture.java similarity index 94% rename from backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/fixture/AuctionForListSearchByTitleAndSortByReliabilityFixture.java rename to backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/fixture/list/AuctionForListSearchByTitleAndSortByReliabilityFixture.java index dfa71bef5..bf025b749 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/fixture/AuctionForListSearchByTitleAndSortByReliabilityFixture.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/fixture/list/AuctionForListSearchByTitleAndSortByReliabilityFixture.java @@ -1,13 +1,14 @@ -package com.ddang.ddang.auction.infrastructure.persistence.fixture; +package com.ddang.ddang.auction.infrastructure.persistence.fixture.list; import com.ddang.ddang.auction.domain.Auction; +import com.ddang.ddang.auction.infrastructure.persistence.fixture.QuerydslAuctionRepositoryForListFixture; import com.ddang.ddang.auction.presentation.dto.request.ReadAuctionSearchCondition; import org.junit.jupiter.api.BeforeEach; import org.springframework.data.domain.Sort; import org.springframework.data.domain.Sort.Order; @SuppressWarnings("NonAsciiCharacters") -public class AuctionForListSearchByTitleAndSortByReliabilityFixture extends QuerydslAuctionRepositoryImplForListFixture { +public class AuctionForListSearchByTitleAndSortByReliabilityFixture extends QuerydslAuctionRepositoryForListFixture { protected Sort 신뢰도순_정렬 = Sort.by(Order.asc("reliability")); protected ReadAuctionSearchCondition 검색어_맥북 = new ReadAuctionSearchCondition("맥북"); diff --git a/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/fixture/list/AuctionForListSearchByTitleFixture.java b/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/fixture/list/AuctionForListSearchByTitleFixture.java new file mode 100644 index 000000000..ab03d1149 --- /dev/null +++ b/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/fixture/list/AuctionForListSearchByTitleFixture.java @@ -0,0 +1,47 @@ +package com.ddang.ddang.auction.infrastructure.persistence.fixture.list; + +import com.ddang.ddang.auction.domain.Auction; +import com.ddang.ddang.auction.infrastructure.persistence.fixture.QuerydslAuctionRepositoryForListFixture; +import com.ddang.ddang.auction.presentation.dto.request.ReadAuctionSearchCondition; +import org.junit.jupiter.api.BeforeEach; + +@SuppressWarnings("NonAsciiCharacters") +public class AuctionForListSearchByTitleFixture extends QuerydslAuctionRepositoryForListFixture { + + protected ReadAuctionSearchCondition 검색어_맥북 = new ReadAuctionSearchCondition("맥북"); + + protected Auction 첫번째_페이지_인덱스_0_맥북_검색_4일_후_마감_id_15; + protected Auction 첫번째_페이지_인덱스_1_맥북_검색_4일_후_마감_id_14; + protected Auction 첫번째_페이지_인덱스_2_맥북_검색_4일_후_마감_id_12; + protected Auction 두번째_페이지_인덱스_0_맥북_검색_4일_후_마감_id_11; + protected Auction 두번째_페이지_인덱스_1_맥북_검색_4일_후_마감_id_10; + protected Auction 두번째_페이지_인덱스_2_맥북_검색_2일_후_마감_id_4; + protected Auction 세번째_페이지_인덱스_0_맥북_검색_3일_후_마감_id_3; + protected Auction 세번째_페이지_인덱스_1_맥북_검색_4일_후_마감_id_2; + protected Auction 세번째_페이지_인덱스_2_맥북_검색_4일_전_마감_id_16; + protected Auction 네번째_페이지_인덱스_0_맥북_검색_4일_전_마감_id_13; + protected Auction 네번째_페이지_인덱스_1_맥북_검색_4일_전_마감_id_9; + protected Auction 네번째_페이지_인덱스_2_맥북_검색_4일_전_마감_id_8; + protected Auction 다섯번째_페이지_인덱스_0_맥북_검색_4일_전_마감_id_7; + protected Auction 다섯번째_페이지_인덱스_1_맥북_검색_5일_전_마감_id_1; + + protected ReadAuctionSearchCondition 검색어_캐비어 = new ReadAuctionSearchCondition("캐비어"); + + @BeforeEach + void fixtureSetUp() { + 첫번째_페이지_인덱스_0_맥북_검색_4일_후_마감_id_15 = 경매15; + 첫번째_페이지_인덱스_1_맥북_검색_4일_후_마감_id_14 = 경매14; + 첫번째_페이지_인덱스_2_맥북_검색_4일_후_마감_id_12 = 경매12; + 두번째_페이지_인덱스_0_맥북_검색_4일_후_마감_id_11 = 경매11; + 두번째_페이지_인덱스_1_맥북_검색_4일_후_마감_id_10 = 경매10; + 두번째_페이지_인덱스_2_맥북_검색_2일_후_마감_id_4 = 경매4; + 세번째_페이지_인덱스_0_맥북_검색_3일_후_마감_id_3 = 경매3; + 세번째_페이지_인덱스_1_맥북_검색_4일_후_마감_id_2 = 경매2; + 세번째_페이지_인덱스_2_맥북_검색_4일_전_마감_id_16 = 경매16; + 네번째_페이지_인덱스_0_맥북_검색_4일_전_마감_id_13 = 경매13; + 네번째_페이지_인덱스_1_맥북_검색_4일_전_마감_id_9 = 경매9; + 네번째_페이지_인덱스_2_맥북_검색_4일_전_마감_id_8 = 경매8; + 다섯번째_페이지_인덱스_0_맥북_검색_4일_전_마감_id_7 = 경매7; + 다섯번째_페이지_인덱스_1_맥북_검색_5일_전_마감_id_1 = 경매1; + } +} diff --git a/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/fixture/AuctionForListSortByAuctioneerCountFixture.java b/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/fixture/list/AuctionForListSortByAuctioneerCountFixture.java similarity index 95% rename from backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/fixture/AuctionForListSortByAuctioneerCountFixture.java rename to backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/fixture/list/AuctionForListSortByAuctioneerCountFixture.java index 7d836df44..9a27dab2a 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/fixture/AuctionForListSortByAuctioneerCountFixture.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/fixture/list/AuctionForListSortByAuctioneerCountFixture.java @@ -1,13 +1,14 @@ -package com.ddang.ddang.auction.infrastructure.persistence.fixture; +package com.ddang.ddang.auction.infrastructure.persistence.fixture.list; import com.ddang.ddang.auction.domain.Auction; +import com.ddang.ddang.auction.infrastructure.persistence.fixture.QuerydslAuctionRepositoryForListFixture; import com.ddang.ddang.auction.presentation.dto.request.ReadAuctionSearchCondition; import org.junit.jupiter.api.BeforeEach; import org.springframework.data.domain.Sort; import org.springframework.data.domain.Sort.Order; @SuppressWarnings("NonAsciiCharacters") -public class AuctionForListSortByAuctioneerCountFixture extends QuerydslAuctionRepositoryImplForListFixture { +public class AuctionForListSortByAuctioneerCountFixture extends QuerydslAuctionRepositoryForListFixture { protected Sort 참여_인원순_정렬 = Sort.by(Order.asc("auctioneerCount")); protected ReadAuctionSearchCondition 검색어_없음 = new ReadAuctionSearchCondition(null); diff --git a/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/fixture/AuctionForListSortByClosingTimeFixture.java b/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/fixture/list/AuctionForListSortByClosingTimeFixture.java similarity index 95% rename from backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/fixture/AuctionForListSortByClosingTimeFixture.java rename to backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/fixture/list/AuctionForListSortByClosingTimeFixture.java index e7cb94697..09f4ed722 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/fixture/AuctionForListSortByClosingTimeFixture.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/fixture/list/AuctionForListSortByClosingTimeFixture.java @@ -1,13 +1,14 @@ -package com.ddang.ddang.auction.infrastructure.persistence.fixture; +package com.ddang.ddang.auction.infrastructure.persistence.fixture.list; import com.ddang.ddang.auction.domain.Auction; +import com.ddang.ddang.auction.infrastructure.persistence.fixture.QuerydslAuctionRepositoryForListFixture; import com.ddang.ddang.auction.presentation.dto.request.ReadAuctionSearchCondition; import org.junit.jupiter.api.BeforeEach; import org.springframework.data.domain.Sort; import org.springframework.data.domain.Sort.Order; @SuppressWarnings("NonAsciiCharacters") -public class AuctionForListSortByClosingTimeFixture extends QuerydslAuctionRepositoryImplForListFixture { +public class AuctionForListSortByClosingTimeFixture extends QuerydslAuctionRepositoryForListFixture { protected Sort 마감_임박순_정렬 = Sort.by(Order.asc("closingTime")); protected ReadAuctionSearchCondition 검색어_없음 = new ReadAuctionSearchCondition(null); diff --git a/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/fixture/list/AuctionForListSortByIdFixture.java b/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/fixture/list/AuctionForListSortByIdFixture.java new file mode 100644 index 000000000..289cc035d --- /dev/null +++ b/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/fixture/list/AuctionForListSortByIdFixture.java @@ -0,0 +1,52 @@ +package com.ddang.ddang.auction.infrastructure.persistence.fixture.list; + +import com.ddang.ddang.auction.domain.Auction; +import com.ddang.ddang.auction.infrastructure.persistence.fixture.QuerydslAuctionRepositoryForListFixture; +import com.ddang.ddang.auction.presentation.dto.request.ReadAuctionSearchCondition; +import org.junit.jupiter.api.BeforeEach; +import org.springframework.data.domain.Sort; +import org.springframework.data.domain.Sort.Order; + +@SuppressWarnings("NonAsciiCharacters") +public class AuctionForListSortByIdFixture extends QuerydslAuctionRepositoryForListFixture { + + protected Sort id순_정렬 = Sort.by(Order.asc("id")); + protected ReadAuctionSearchCondition 검색어_없음 = new ReadAuctionSearchCondition(null); + + protected Auction 첫번째_페이지_인덱스_0_4일_후_마감_id_15; + protected Auction 첫번째_페이지_인덱스_1_4일_후_마감_id_14; + protected Auction 첫번째_페이지_인덱스_2_4일_후_마감_id_12; + protected Auction 두번째_페이지_인덱스_0_4일_후_마감_id_11; + protected Auction 두번째_페이지_인덱스_1_4일_후_마감_id_10; + protected Auction 두번째_페이지_인덱스_2_1일_후_마감_id_5; + protected Auction 세번째_페이지_인덱스_0_2일_후_마감_id_4; + protected Auction 세번째_페이지_인덱스_1_3일_후_마감_id_3; + protected Auction 세번째_페이지_인덱스_2_4일_후_마감_id_2; + protected Auction 네번째_페이지_인덱스_0_4일_전_마감_id_16; + protected Auction 네번째_페이지_인덱스_1_4일_전_마감_id_13; + protected Auction 네번째_페이지_인덱스_2_4일_전_마감_id_9; + protected Auction 다섯번째_페이지_인덱스_0_4일_전_마감_id_8; + protected Auction 다섯번째_페이지_인덱스_1_4일_전_마감_id_7; + protected Auction 다섯번째_페이지_인덱스_2_2일_전_마감_id_6; + protected Auction 여섯번째_페이지_인덱스_0_5일_전_마감_id_1; + + @BeforeEach + void fixtureSetUp() { + 첫번째_페이지_인덱스_0_4일_후_마감_id_15 = 경매15; + 첫번째_페이지_인덱스_1_4일_후_마감_id_14 = 경매14; + 첫번째_페이지_인덱스_2_4일_후_마감_id_12 = 경매12; + 두번째_페이지_인덱스_0_4일_후_마감_id_11 = 경매11; + 두번째_페이지_인덱스_1_4일_후_마감_id_10 = 경매10; + 두번째_페이지_인덱스_2_1일_후_마감_id_5 = 경매5; + 세번째_페이지_인덱스_0_2일_후_마감_id_4 = 경매4; + 세번째_페이지_인덱스_1_3일_후_마감_id_3 = 경매3; + 세번째_페이지_인덱스_2_4일_후_마감_id_2 = 경매2; + 네번째_페이지_인덱스_0_4일_전_마감_id_16 = 경매16; + 네번째_페이지_인덱스_1_4일_전_마감_id_13 = 경매13; + 네번째_페이지_인덱스_2_4일_전_마감_id_9 = 경매9; + 다섯번째_페이지_인덱스_0_4일_전_마감_id_8 = 경매8; + 다섯번째_페이지_인덱스_1_4일_전_마감_id_7 = 경매7; + 여섯번째_페이지_인덱스_0_5일_전_마감_id_1 = 경매1; + 다섯번째_페이지_인덱스_2_2일_전_마감_id_6 = 경매6; + } +} diff --git a/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/fixture/AuctionForListSortByReliabilityFixture.java b/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/fixture/list/AuctionForListSortByReliabilityFixture.java similarity index 95% rename from backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/fixture/AuctionForListSortByReliabilityFixture.java rename to backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/fixture/list/AuctionForListSortByReliabilityFixture.java index 45f0a9b7a..1e5678b6a 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/fixture/AuctionForListSortByReliabilityFixture.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/fixture/list/AuctionForListSortByReliabilityFixture.java @@ -1,13 +1,14 @@ -package com.ddang.ddang.auction.infrastructure.persistence.fixture; +package com.ddang.ddang.auction.infrastructure.persistence.fixture.list; import com.ddang.ddang.auction.domain.Auction; +import com.ddang.ddang.auction.infrastructure.persistence.fixture.QuerydslAuctionRepositoryForListFixture; import com.ddang.ddang.auction.presentation.dto.request.ReadAuctionSearchCondition; import org.junit.jupiter.api.BeforeEach; import org.springframework.data.domain.Sort; import org.springframework.data.domain.Sort.Order; @SuppressWarnings("NonAsciiCharacters") -public class AuctionForListSortByReliabilityFixture extends QuerydslAuctionRepositoryImplForListFixture { +public class AuctionForListSortByReliabilityFixture extends QuerydslAuctionRepositoryForListFixture { protected Sort 신뢰도순_정렬 = Sort.by(Order.asc("reliability")); protected ReadAuctionSearchCondition 검색어_없음 = new ReadAuctionSearchCondition(null); diff --git a/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/AuctionForListByBidderIdTest.java b/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/list/AuctionForListByBidderIdTest.java similarity index 96% rename from backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/AuctionForListByBidderIdTest.java rename to backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/list/AuctionForListByBidderIdTest.java index 9ef4bdf99..90edc7aa5 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/AuctionForListByBidderIdTest.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/list/AuctionForListByBidderIdTest.java @@ -1,7 +1,8 @@ -package com.ddang.ddang.auction.infrastructure.persistence; +package com.ddang.ddang.auction.infrastructure.persistence.list; import com.ddang.ddang.auction.domain.Auction; -import com.ddang.ddang.auction.infrastructure.persistence.fixture.AuctionForListByBidderIdFixture; +import com.ddang.ddang.auction.infrastructure.persistence.QuerydslAuctionRepository; +import com.ddang.ddang.auction.infrastructure.persistence.fixture.list.AuctionForListByBidderIdFixture; import com.ddang.ddang.configuration.JpaConfiguration; import com.ddang.ddang.configuration.QuerydslConfiguration; import com.querydsl.jpa.impl.JPAQueryFactory; @@ -27,7 +28,7 @@ class AuctionForListByBidderIdTest extends AuctionForListByBidderIdFixture { @BeforeEach void setUp(@Autowired final JPAQueryFactory queryFactory) { - querydslAuctionRepository = new QuerydslAuctionRepositoryImpl(queryFactory); + querydslAuctionRepository = new QuerydslAuctionRepository(queryFactory); } @Nested diff --git a/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/AuctionForListByUserIdTest.java b/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/list/AuctionForListByUserIdTest.java similarity index 95% rename from backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/AuctionForListByUserIdTest.java rename to backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/list/AuctionForListByUserIdTest.java index 5c200dc28..7d81822f7 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/AuctionForListByUserIdTest.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/list/AuctionForListByUserIdTest.java @@ -1,7 +1,8 @@ -package com.ddang.ddang.auction.infrastructure.persistence; +package com.ddang.ddang.auction.infrastructure.persistence.list; import com.ddang.ddang.auction.domain.Auction; -import com.ddang.ddang.auction.infrastructure.persistence.fixture.AuctionForListByUserIdFixture; +import com.ddang.ddang.auction.infrastructure.persistence.QuerydslAuctionRepository; +import com.ddang.ddang.auction.infrastructure.persistence.fixture.list.AuctionForListByUserIdFixture; import com.ddang.ddang.configuration.JpaConfiguration; import com.ddang.ddang.configuration.QuerydslConfiguration; import com.querydsl.jpa.impl.JPAQueryFactory; @@ -27,7 +28,7 @@ class AuctionForListByUserIdTest extends AuctionForListByUserIdFixture { @BeforeEach void setUp(@Autowired final JPAQueryFactory queryFactory) { - querydslAuctionRepository = new QuerydslAuctionRepositoryImpl(queryFactory); + querydslAuctionRepository = new QuerydslAuctionRepository(queryFactory); } @Nested diff --git a/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/AuctionForListSearchByTitleAndSortByAuctioneerCountTest.java b/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/list/AuctionForListSearchByTitleAndSortByAuctioneerCountTest.java similarity index 92% rename from backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/AuctionForListSearchByTitleAndSortByAuctioneerCountTest.java rename to backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/list/AuctionForListSearchByTitleAndSortByAuctioneerCountTest.java index ebf8de098..17ea95c66 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/AuctionForListSearchByTitleAndSortByAuctioneerCountTest.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/list/AuctionForListSearchByTitleAndSortByAuctioneerCountTest.java @@ -1,7 +1,8 @@ -package com.ddang.ddang.auction.infrastructure.persistence; +package com.ddang.ddang.auction.infrastructure.persistence.list; import com.ddang.ddang.auction.domain.Auction; -import com.ddang.ddang.auction.infrastructure.persistence.fixture.AuctionForListSearchByTitleAndSortByAuctioneerCountFixture; +import com.ddang.ddang.auction.infrastructure.persistence.QuerydslAuctionRepository; +import com.ddang.ddang.auction.infrastructure.persistence.fixture.list.AuctionForListSearchByTitleAndSortByAuctioneerCountFixture; import com.ddang.ddang.configuration.JpaConfiguration; import com.ddang.ddang.configuration.QuerydslConfiguration; import com.querydsl.jpa.impl.JPAQueryFactory; @@ -26,15 +27,15 @@ class AuctionForListSearchByTitleAndSortByAuctioneerCountTest extends AuctionFor @BeforeEach void setUp(@Autowired final JPAQueryFactory queryFactory) { - querydslAuctionRepository = new QuerydslAuctionRepositoryImpl(queryFactory); + querydslAuctionRepository = new QuerydslAuctionRepository(queryFactory); } @Test void 첫번째_페이지_요청_테스트() { // when final Slice actual = querydslAuctionRepository.findAuctionsAllByCondition( - PageRequest.of(0, 페이지_크기_3, 참여_인원순_정렬), - 검색어_맥북 + 검색어_맥북, + PageRequest.of(0, 페이지_크기_3, 참여_인원순_정렬) ); // then @@ -51,8 +52,8 @@ void setUp(@Autowired final JPAQueryFactory queryFactory) { void 두번째_페이지_요청_테스트() { // when final Slice actual = querydslAuctionRepository.findAuctionsAllByCondition( - PageRequest.of(1, 페이지_크기_3, 참여_인원순_정렬), - 검색어_맥북 + 검색어_맥북, + PageRequest.of(1, 페이지_크기_3, 참여_인원순_정렬) ); // then @@ -69,8 +70,8 @@ void setUp(@Autowired final JPAQueryFactory queryFactory) { void 세번째_페이지_요청_테스트() { // when final Slice actual = querydslAuctionRepository.findAuctionsAllByCondition( - PageRequest.of(2, 페이지_크기_3, 참여_인원순_정렬), - 검색어_맥북 + 검색어_맥북, + PageRequest.of(2, 페이지_크기_3, 참여_인원순_정렬) ); // then @@ -87,8 +88,8 @@ void setUp(@Autowired final JPAQueryFactory queryFactory) { void 네번째_페이지_요청_테스트() { // when final Slice actual = querydslAuctionRepository.findAuctionsAllByCondition( - PageRequest.of(3, 페이지_크기_3, 참여_인원순_정렬), - 검색어_맥북 + 검색어_맥북, + PageRequest.of(3, 페이지_크기_3, 참여_인원순_정렬) ); // then @@ -105,8 +106,8 @@ void setUp(@Autowired final JPAQueryFactory queryFactory) { void 다섯번째_페이지_요청_테스트() { // when final Slice actual = querydslAuctionRepository.findAuctionsAllByCondition( - PageRequest.of(4, 페이지_크기_3, 참여_인원순_정렬), - 검색어_맥북 + 검색어_맥북, + PageRequest.of(4, 페이지_크기_3, 참여_인원순_정렬) ); SoftAssertions.assertSoftly(softAssertions -> { @@ -121,8 +122,8 @@ void setUp(@Autowired final JPAQueryFactory queryFactory) { void 페이지_크기_3_여섯번째_페이지_요청_테스트() { // when final Slice actual = querydslAuctionRepository.findAuctionsAllByCondition( - PageRequest.of(5, 페이지_크기_3, 참여_인원순_정렬), - 검색어_맥북 + 검색어_맥북, + PageRequest.of(5, 페이지_크기_3, 참여_인원순_정렬) ); // then diff --git a/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/AuctionForListSearchByTitleAndSortByClosingTimeTest.java b/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/list/AuctionForListSearchByTitleAndSortByClosingTimeTest.java similarity index 91% rename from backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/AuctionForListSearchByTitleAndSortByClosingTimeTest.java rename to backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/list/AuctionForListSearchByTitleAndSortByClosingTimeTest.java index 5c89f96a4..50380aff3 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/AuctionForListSearchByTitleAndSortByClosingTimeTest.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/list/AuctionForListSearchByTitleAndSortByClosingTimeTest.java @@ -1,7 +1,8 @@ -package com.ddang.ddang.auction.infrastructure.persistence; +package com.ddang.ddang.auction.infrastructure.persistence.list; import com.ddang.ddang.auction.domain.Auction; -import com.ddang.ddang.auction.infrastructure.persistence.fixture.AuctionForListSearchByTitleAndSortByClosingTimeFixture; +import com.ddang.ddang.auction.infrastructure.persistence.QuerydslAuctionRepository; +import com.ddang.ddang.auction.infrastructure.persistence.fixture.list.AuctionForListSearchByTitleAndSortByClosingTimeFixture; import com.ddang.ddang.configuration.JpaConfiguration; import com.ddang.ddang.configuration.QuerydslConfiguration; import com.querydsl.jpa.impl.JPAQueryFactory; @@ -26,15 +27,15 @@ class AuctionForListSearchByTitleAndSortByClosingTimeTest extends AuctionForList @BeforeEach void setUp(@Autowired final JPAQueryFactory queryFactory) { - querydslAuctionRepository = new QuerydslAuctionRepositoryImpl(queryFactory); + querydslAuctionRepository = new QuerydslAuctionRepository(queryFactory); } @Test void 첫번째_페이지_요청_테스트() { // when final Slice actual = querydslAuctionRepository.findAuctionsAllByCondition( - PageRequest.of(0, 페이지_크기_3, 마감_임박순_정렬), - 검색어_맥북 + 검색어_맥북, + PageRequest.of(0, 페이지_크기_3, 마감_임박순_정렬) ); // then @@ -51,8 +52,8 @@ void setUp(@Autowired final JPAQueryFactory queryFactory) { void 두번째_페이지_요청_테스트() { // when final Slice actual = querydslAuctionRepository.findAuctionsAllByCondition( - PageRequest.of(1, 페이지_크기_3, 마감_임박순_정렬), - 검색어_맥북 + 검색어_맥북, + PageRequest.of(1, 페이지_크기_3, 마감_임박순_정렬) ); // then @@ -69,8 +70,8 @@ void setUp(@Autowired final JPAQueryFactory queryFactory) { void 세번째_페이지_요청_테스트() { // when final Slice actual = querydslAuctionRepository.findAuctionsAllByCondition( - PageRequest.of(2, 페이지_크기_3, 마감_임박순_정렬), - 검색어_맥북 + 검색어_맥북, + PageRequest.of(2, 페이지_크기_3, 마감_임박순_정렬) ); // then @@ -87,8 +88,8 @@ void setUp(@Autowired final JPAQueryFactory queryFactory) { void 네번째_페이지_요청_테스트() { // when final Slice actual = querydslAuctionRepository.findAuctionsAllByCondition( - PageRequest.of(3, 페이지_크기_3, 마감_임박순_정렬), - 검색어_맥북 + 검색어_맥북, + PageRequest.of(3, 페이지_크기_3, 마감_임박순_정렬) ); // then @@ -105,8 +106,8 @@ void setUp(@Autowired final JPAQueryFactory queryFactory) { void 다섯번째_페이지_요청_테스트() { // when final Slice actual = querydslAuctionRepository.findAuctionsAllByCondition( - PageRequest.of(4, 페이지_크기_3, 마감_임박순_정렬), - 검색어_맥북 + 검색어_맥북, + PageRequest.of(4, 페이지_크기_3, 마감_임박순_정렬) ); SoftAssertions.assertSoftly(softAssertions -> { @@ -121,8 +122,8 @@ void setUp(@Autowired final JPAQueryFactory queryFactory) { void 페이지_크기_3_여섯번째_페이지_요청_테스트() { // when final Slice actual = querydslAuctionRepository.findAuctionsAllByCondition( - PageRequest.of(5, 페이지_크기_3, 마감_임박순_정렬), - 검색어_맥북 + 검색어_맥북, + PageRequest.of(5, 페이지_크기_3, 마감_임박순_정렬) ); // then diff --git a/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/AuctionForListSearchByTitleAndSortByIdTest.java b/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/list/AuctionForListSearchByTitleAndSortByIdTest.java similarity index 78% rename from backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/AuctionForListSearchByTitleAndSortByIdTest.java rename to backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/list/AuctionForListSearchByTitleAndSortByIdTest.java index cbc0926b8..24129045d 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/AuctionForListSearchByTitleAndSortByIdTest.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/list/AuctionForListSearchByTitleAndSortByIdTest.java @@ -1,7 +1,8 @@ -package com.ddang.ddang.auction.infrastructure.persistence; +package com.ddang.ddang.auction.infrastructure.persistence.list; import com.ddang.ddang.auction.domain.Auction; -import com.ddang.ddang.auction.infrastructure.persistence.fixture.AuctionForListSearchByTitleAndSortByIdFixture; +import com.ddang.ddang.auction.infrastructure.persistence.QuerydslAuctionRepository; +import com.ddang.ddang.auction.infrastructure.persistence.fixture.list.AuctionForListSearchByTitleAndSortByIdFixture; import com.ddang.ddang.configuration.JpaConfiguration; import com.ddang.ddang.configuration.QuerydslConfiguration; import com.querydsl.jpa.impl.JPAQueryFactory; @@ -26,23 +27,23 @@ class AuctionForListSearchByTitleAndSortByIdTest extends AuctionForListSearchByT @BeforeEach void setUp(@Autowired final JPAQueryFactory queryFactory) { - querydslAuctionRepository = new QuerydslAuctionRepositoryImpl(queryFactory); + querydslAuctionRepository = new QuerydslAuctionRepository(queryFactory); } @Test void 첫번째_페이지_요청_테스트() { // when final Slice actual = querydslAuctionRepository.findAuctionsAllByCondition( - PageRequest.of(0, 페이지_크기_3, id순_정렬), - 검색어_맥북 + 검색어_맥북, + PageRequest.of(0, 페이지_크기_3, id순_정렬) ); // then SoftAssertions.assertSoftly(softAssertions -> { softAssertions.assertThat(actual).hasSize(3); - softAssertions.assertThat(actual.getContent().get(0)).isEqualTo(첫번째_페이지_인덱스_0_id_16); - softAssertions.assertThat(actual.getContent().get(1)).isEqualTo(첫번째_페이지_인덱스_1_id_15); - softAssertions.assertThat(actual.getContent().get(2)).isEqualTo(첫번째_페이지_인덱스_2_id_14); + softAssertions.assertThat(actual.getContent().get(0)).isEqualTo(첫번째_페이지_인덱스_0_4일_후_마감_id_15); + softAssertions.assertThat(actual.getContent().get(1)).isEqualTo(첫번째_페이지_인덱스_1_4일_후_마감_id_14); + softAssertions.assertThat(actual.getContent().get(2)).isEqualTo(첫번째_페이지_인덱스_2_4일_후_마감_id_12); softAssertions.assertThat(actual.hasNext()).isTrue(); }); } @@ -51,16 +52,16 @@ void setUp(@Autowired final JPAQueryFactory queryFactory) { void 두번째_페이지_요청_테스트() { // when final Slice actual = querydslAuctionRepository.findAuctionsAllByCondition( - PageRequest.of(1, 페이지_크기_3, id순_정렬), - 검색어_맥북 + 검색어_맥북, + PageRequest.of(1, 페이지_크기_3, id순_정렬) ); // then SoftAssertions.assertSoftly(softAssertions -> { softAssertions.assertThat(actual).hasSize(3); - softAssertions.assertThat(actual.getContent().get(0)).isEqualTo(두번째_페이지_인덱스_0_id_13); - softAssertions.assertThat(actual.getContent().get(1)).isEqualTo(두번째_페이지_인덱스_1_id_12); - softAssertions.assertThat(actual.getContent().get(2)).isEqualTo(두번째_페이지_인덱스_2_id_11); + softAssertions.assertThat(actual.getContent().get(0)).isEqualTo(두번째_페이지_인덱스_0_4일_후_마감_id_11); + softAssertions.assertThat(actual.getContent().get(1)).isEqualTo(두번째_페이지_인덱스_1_4일_후_마감_id_10); + softAssertions.assertThat(actual.getContent().get(2)).isEqualTo(두번째_페이지_인덱스_2_2일_후_마감_id_4); softAssertions.assertThat(actual.hasNext()).isTrue(); }); } @@ -69,16 +70,16 @@ void setUp(@Autowired final JPAQueryFactory queryFactory) { void 세번째_페이지_요청_테스트() { // when final Slice actual = querydslAuctionRepository.findAuctionsAllByCondition( - PageRequest.of(2, 페이지_크기_3, id순_정렬), - 검색어_맥북 + 검색어_맥북, + PageRequest.of(2, 페이지_크기_3, id순_정렬) ); // then SoftAssertions.assertSoftly(softAssertions -> { softAssertions.assertThat(actual).hasSize(3); - softAssertions.assertThat(actual.getContent().get(0)).isEqualTo(세번째_페이지_인덱스_0_id_10); - softAssertions.assertThat(actual.getContent().get(1)).isEqualTo(세번째_페이지_인덱스_1_id_9); - softAssertions.assertThat(actual.getContent().get(2)).isEqualTo(세번째_페이지_인덱스_2_id_8); + softAssertions.assertThat(actual.getContent().get(0)).isEqualTo(세번째_페이지_인덱스_0_3일_후_마감_id_3); + softAssertions.assertThat(actual.getContent().get(1)).isEqualTo(세번째_페이지_인덱스_1_4일_후_마감_id_2); + softAssertions.assertThat(actual.getContent().get(2)).isEqualTo(세번째_페이지_인덱스_2_4일_전_마감_id_16); softAssertions.assertThat(actual.hasNext()).isTrue(); }); } @@ -87,16 +88,16 @@ void setUp(@Autowired final JPAQueryFactory queryFactory) { void 네번째_페이지_요청_테스트() { // when final Slice actual = querydslAuctionRepository.findAuctionsAllByCondition( - PageRequest.of(3, 페이지_크기_3, id순_정렬), - 검색어_맥북 + 검색어_맥북, + PageRequest.of(3, 페이지_크기_3, id순_정렬) ); // then SoftAssertions.assertSoftly(softAssertions -> { softAssertions.assertThat(actual).hasSize(3); - softAssertions.assertThat(actual.getContent().get(0)).isEqualTo(네번째_페이지_인덱스_0_id_7); - softAssertions.assertThat(actual.getContent().get(1)).isEqualTo(네번째_페이지_인덱스_1_id_4); - softAssertions.assertThat(actual.getContent().get(2)).isEqualTo(네번째_페이지_인덱스_2_id_3); + softAssertions.assertThat(actual.getContent().get(0)).isEqualTo(네번째_페이지_인덱스_0_4일_전_마감_id_13); + softAssertions.assertThat(actual.getContent().get(1)).isEqualTo(네번째_페이지_인덱스_1_4일_전_마감_id_9); + softAssertions.assertThat(actual.getContent().get(2)).isEqualTo(네번째_페이지_인덱스_2_4일_전_마감_id_8); softAssertions.assertThat(actual.hasNext()).isTrue(); }); } @@ -105,14 +106,14 @@ void setUp(@Autowired final JPAQueryFactory queryFactory) { void 다섯번째_페이지_요청_테스트() { // when final Slice actual = querydslAuctionRepository.findAuctionsAllByCondition( - PageRequest.of(4, 페이지_크기_3, id순_정렬), - 검색어_맥북 + 검색어_맥북, + PageRequest.of(4, 페이지_크기_3, id순_정렬) ); SoftAssertions.assertSoftly(softAssertions -> { softAssertions.assertThat(actual).hasSize(2); - softAssertions.assertThat(actual.getContent().get(0)).isEqualTo(다섯번째_페이지_인덱스_0_id_2); - softAssertions.assertThat(actual.getContent().get(1)).isEqualTo(다섯번째_페이지_인덱스_1_id_1); + softAssertions.assertThat(actual.getContent().get(0)).isEqualTo(다섯번째_페이지_인덱스_0_4일_전_마감_id_7); + softAssertions.assertThat(actual.getContent().get(1)).isEqualTo(다섯번째_페이지_인덱스_1_5일_전_마감_id_1); softAssertions.assertThat(actual.hasNext()).isFalse(); }); } @@ -121,8 +122,8 @@ void setUp(@Autowired final JPAQueryFactory queryFactory) { void 페이지_크기_3_여섯번째_페이지_요청_테스트() { // when final Slice actual = querydslAuctionRepository.findAuctionsAllByCondition( - PageRequest.of(5, 페이지_크기_3, id순_정렬), - 검색어_맥북 + 검색어_맥북, + PageRequest.of(5, 페이지_크기_3, id순_정렬) ); // then diff --git a/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/AuctionForListSearchByTitleAndSortByReliabilityTest.java b/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/list/AuctionForListSearchByTitleAndSortByReliabilityTest.java similarity index 92% rename from backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/AuctionForListSearchByTitleAndSortByReliabilityTest.java rename to backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/list/AuctionForListSearchByTitleAndSortByReliabilityTest.java index 573a53bd4..b3a8503cb 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/AuctionForListSearchByTitleAndSortByReliabilityTest.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/list/AuctionForListSearchByTitleAndSortByReliabilityTest.java @@ -1,7 +1,8 @@ -package com.ddang.ddang.auction.infrastructure.persistence; +package com.ddang.ddang.auction.infrastructure.persistence.list; import com.ddang.ddang.auction.domain.Auction; -import com.ddang.ddang.auction.infrastructure.persistence.fixture.AuctionForListSearchByTitleAndSortByReliabilityFixture; +import com.ddang.ddang.auction.infrastructure.persistence.QuerydslAuctionRepository; +import com.ddang.ddang.auction.infrastructure.persistence.fixture.list.AuctionForListSearchByTitleAndSortByReliabilityFixture; import com.ddang.ddang.configuration.JpaConfiguration; import com.ddang.ddang.configuration.QuerydslConfiguration; import com.querydsl.jpa.impl.JPAQueryFactory; @@ -26,15 +27,15 @@ class AuctionForListSearchByTitleAndSortByReliabilityTest extends AuctionForList @BeforeEach void setUp(@Autowired final JPAQueryFactory queryFactory) { - querydslAuctionRepository = new QuerydslAuctionRepositoryImpl(queryFactory); + querydslAuctionRepository = new QuerydslAuctionRepository(queryFactory); } @Test void 페이지_크기_3_첫번째_페이지_요청_테스트() { // when final Slice actual = querydslAuctionRepository.findAuctionsAllByCondition( - PageRequest.of(0, 페이지_크기_3, 신뢰도순_정렬), - 검색어_맥북 + 검색어_맥북, + PageRequest.of(0, 페이지_크기_3, 신뢰도순_정렬) ); // then @@ -51,8 +52,8 @@ void setUp(@Autowired final JPAQueryFactory queryFactory) { void 페이지_크기_3_두번째_페이지_요청_테스트() { // when final Slice actual = querydslAuctionRepository.findAuctionsAllByCondition( - PageRequest.of(1, 페이지_크기_3, 신뢰도순_정렬), - 검색어_맥북 + 검색어_맥북, + PageRequest.of(1, 페이지_크기_3, 신뢰도순_정렬) ); // then @@ -69,8 +70,8 @@ void setUp(@Autowired final JPAQueryFactory queryFactory) { void 페이지_크기_3_세번째_페이지_요청_테스트() { // when final Slice actual = querydslAuctionRepository.findAuctionsAllByCondition( - PageRequest.of(2, 페이지_크기_3, 신뢰도순_정렬), - 검색어_맥북 + 검색어_맥북, + PageRequest.of(2, 페이지_크기_3, 신뢰도순_정렬) ); // then @@ -87,8 +88,8 @@ void setUp(@Autowired final JPAQueryFactory queryFactory) { void 페이지_크기_3_네번째_페이지_요청_테스트() { // when final Slice actual = querydslAuctionRepository.findAuctionsAllByCondition( - PageRequest.of(3, 페이지_크기_3, 신뢰도순_정렬), - 검색어_맥북 + 검색어_맥북, + PageRequest.of(3, 페이지_크기_3, 신뢰도순_정렬) ); // then @@ -105,8 +106,8 @@ void setUp(@Autowired final JPAQueryFactory queryFactory) { void 페이지_크기_3_다섯번째_페이지_요청_테스트() { // when final Slice actual = querydslAuctionRepository.findAuctionsAllByCondition( - PageRequest.of(4, 페이지_크기_3, 신뢰도순_정렬), - 검색어_맥북 + 검색어_맥북, + PageRequest.of(4, 페이지_크기_3, 신뢰도순_정렬) ); SoftAssertions.assertSoftly(softAssertions -> { @@ -121,8 +122,8 @@ void setUp(@Autowired final JPAQueryFactory queryFactory) { void 페이지_크기_3_여섯번째_페이지_요청_테스트() { // when final Slice actual = querydslAuctionRepository.findAuctionsAllByCondition( - PageRequest.of(5, 페이지_크기_3, 신뢰도순_정렬), - 검색어_맥북 + 검색어_맥북, + PageRequest.of(5, 페이지_크기_3, 신뢰도순_정렬) ); // then diff --git a/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/AuctionForListSearchByTitleTest.java b/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/list/AuctionForListSearchByTitleTest.java similarity index 74% rename from backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/AuctionForListSearchByTitleTest.java rename to backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/list/AuctionForListSearchByTitleTest.java index 0364d7c8d..fb36cb88b 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/AuctionForListSearchByTitleTest.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/list/AuctionForListSearchByTitleTest.java @@ -1,7 +1,8 @@ -package com.ddang.ddang.auction.infrastructure.persistence; +package com.ddang.ddang.auction.infrastructure.persistence.list; import com.ddang.ddang.auction.domain.Auction; -import com.ddang.ddang.auction.infrastructure.persistence.fixture.AuctionForListSearchByTitleFixture; +import com.ddang.ddang.auction.infrastructure.persistence.QuerydslAuctionRepository; +import com.ddang.ddang.auction.infrastructure.persistence.fixture.list.AuctionForListSearchByTitleFixture; import com.ddang.ddang.configuration.JpaConfiguration; import com.ddang.ddang.configuration.QuerydslConfiguration; import com.querydsl.jpa.impl.JPAQueryFactory; @@ -27,7 +28,7 @@ class AuctionForListSearchByTitleTest extends AuctionForListSearchByTitleFixture @BeforeEach void setUp(@Autowired final JPAQueryFactory queryFactory) { - querydslAuctionRepository = new QuerydslAuctionRepositoryImpl(queryFactory); + querydslAuctionRepository = new QuerydslAuctionRepository(queryFactory); } @Nested @@ -37,16 +38,16 @@ class 검색_결과가_14개인_검색어_테스트 { void 페이지_크기_3_첫번째_페이지_요청_테스트() { // when final Slice actual = querydslAuctionRepository.findAuctionsAllByCondition( - PageRequest.of(0, 페이지_크기_3), - 검색어_맥북 + 검색어_맥북, + PageRequest.of(0, 페이지_크기_3) ); // then SoftAssertions.assertSoftly(softAssertions -> { softAssertions.assertThat(actual).hasSize(3); - softAssertions.assertThat(actual.getContent().get(0)).isEqualTo(첫번째_페이지_인덱스_0_맥북_검색_id_16); - softAssertions.assertThat(actual.getContent().get(1)).isEqualTo(첫번째_페이지_인덱스_1_맥북_검색_id_15); - softAssertions.assertThat(actual.getContent().get(2)).isEqualTo(첫번째_페이지_인덱스_2_맥북_검색_id_14); + softAssertions.assertThat(actual.getContent().get(0)).isEqualTo(첫번째_페이지_인덱스_0_맥북_검색_4일_후_마감_id_15); + softAssertions.assertThat(actual.getContent().get(1)).isEqualTo(첫번째_페이지_인덱스_1_맥북_검색_4일_후_마감_id_14); + softAssertions.assertThat(actual.getContent().get(2)).isEqualTo(첫번째_페이지_인덱스_2_맥북_검색_4일_후_마감_id_12); softAssertions.assertThat(actual.hasNext()).isTrue(); }); } @@ -55,16 +56,16 @@ class 검색_결과가_14개인_검색어_테스트 { void 페이지_크기_3_두번째_페이지_요청_테스트() { // when final Slice actual = querydslAuctionRepository.findAuctionsAllByCondition( - PageRequest.of(1, 페이지_크기_3), - 검색어_맥북 + 검색어_맥북, + PageRequest.of(1, 페이지_크기_3) ); // then SoftAssertions.assertSoftly(softAssertions -> { softAssertions.assertThat(actual).hasSize(3); - softAssertions.assertThat(actual.getContent().get(0)).isEqualTo(두번째_페이지_인덱스_0_맥북_검색_id_13); - softAssertions.assertThat(actual.getContent().get(1)).isEqualTo(두번째_페이지_인덱스_1_맥북_검색_id_12); - softAssertions.assertThat(actual.getContent().get(2)).isEqualTo(두번째_페이지_인덱스_2_맥북_검색_id_11); + softAssertions.assertThat(actual.getContent().get(0)).isEqualTo(두번째_페이지_인덱스_0_맥북_검색_4일_후_마감_id_11); + softAssertions.assertThat(actual.getContent().get(1)).isEqualTo(두번째_페이지_인덱스_1_맥북_검색_4일_후_마감_id_10); + softAssertions.assertThat(actual.getContent().get(2)).isEqualTo(두번째_페이지_인덱스_2_맥북_검색_2일_후_마감_id_4); softAssertions.assertThat(actual.hasNext()).isTrue(); }); } @@ -73,16 +74,16 @@ class 검색_결과가_14개인_검색어_테스트 { void 페이지_크기_3_세번째_페이지_요청_테스트() { // when final Slice actual = querydslAuctionRepository.findAuctionsAllByCondition( - PageRequest.of(2, 페이지_크기_3), - 검색어_맥북 + 검색어_맥북, + PageRequest.of(2, 페이지_크기_3) ); // then SoftAssertions.assertSoftly(softAssertions -> { softAssertions.assertThat(actual).hasSize(3); - softAssertions.assertThat(actual.getContent().get(0)).isEqualTo(세번째_페이지_인덱스_0_맥북_검색_id_10); - softAssertions.assertThat(actual.getContent().get(1)).isEqualTo(세번째_페이지_인덱스_1_맥북_검색_id_9); - softAssertions.assertThat(actual.getContent().get(2)).isEqualTo(세번째_페이지_인덱스_2_맥북_검색_id_8); + softAssertions.assertThat(actual.getContent().get(0)).isEqualTo(세번째_페이지_인덱스_0_맥북_검색_3일_후_마감_id_3); + softAssertions.assertThat(actual.getContent().get(1)).isEqualTo(세번째_페이지_인덱스_1_맥북_검색_4일_후_마감_id_2); + softAssertions.assertThat(actual.getContent().get(2)).isEqualTo(세번째_페이지_인덱스_2_맥북_검색_4일_전_마감_id_16); softAssertions.assertThat(actual.hasNext()).isTrue(); }); } @@ -91,16 +92,16 @@ class 검색_결과가_14개인_검색어_테스트 { void 페이지_크기_3_네번째_페이지_요청_테스트() { // when final Slice actual = querydslAuctionRepository.findAuctionsAllByCondition( - PageRequest.of(3, 페이지_크기_3), - 검색어_맥북 + 검색어_맥북, + PageRequest.of(3, 페이지_크기_3) ); // then SoftAssertions.assertSoftly(softAssertions -> { softAssertions.assertThat(actual).hasSize(3); - softAssertions.assertThat(actual.getContent().get(0)).isEqualTo(네번째_페이지_인덱스_0_맥북_검색_id_7); - softAssertions.assertThat(actual.getContent().get(1)).isEqualTo(네번째_페이지_인덱스_1_맥북_검색_id_4); - softAssertions.assertThat(actual.getContent().get(2)).isEqualTo(네번째_페이지_인덱스_2_맥북_검색_id_3); + softAssertions.assertThat(actual.getContent().get(0)).isEqualTo(네번째_페이지_인덱스_0_맥북_검색_4일_전_마감_id_13); + softAssertions.assertThat(actual.getContent().get(1)).isEqualTo(네번째_페이지_인덱스_1_맥북_검색_4일_전_마감_id_9); + softAssertions.assertThat(actual.getContent().get(2)).isEqualTo(네번째_페이지_인덱스_2_맥북_검색_4일_전_마감_id_8); softAssertions.assertThat(actual.hasNext()).isTrue(); }); } @@ -109,15 +110,15 @@ class 검색_결과가_14개인_검색어_테스트 { void 페이지_크기_3_다섯번째_페이지_요청_테스트() { // when final Slice actual = querydslAuctionRepository.findAuctionsAllByCondition( - PageRequest.of(4, 페이지_크기_3), - 검색어_맥북 + 검색어_맥북, + PageRequest.of(4, 페이지_크기_3) ); // then SoftAssertions.assertSoftly(softAssertions -> { softAssertions.assertThat(actual).hasSize(2); - softAssertions.assertThat(actual.getContent().get(0)).isEqualTo(다섯번째_페이지_인덱스_0_맥북_검색_id_2); - softAssertions.assertThat(actual.getContent().get(1)).isEqualTo(다섯번째_페이지_인덱스_1_맥북_검색_id_1); + softAssertions.assertThat(actual.getContent().get(0)).isEqualTo(다섯번째_페이지_인덱스_0_맥북_검색_4일_전_마감_id_7); + softAssertions.assertThat(actual.getContent().get(1)).isEqualTo(다섯번째_페이지_인덱스_1_맥북_검색_5일_전_마감_id_1); softAssertions.assertThat(actual.hasNext()).isFalse(); }); } @@ -126,8 +127,8 @@ class 검색_결과가_14개인_검색어_테스트 { void 페이지_크기_3_여섯번째_페이지_요청_테스트() { // when final Slice actual = querydslAuctionRepository.findAuctionsAllByCondition( - PageRequest.of(5, 페이지_크기_3), - 검색어_맥북 + 검색어_맥북, + PageRequest.of(5, 페이지_크기_3) ); // then @@ -146,8 +147,8 @@ class 검색_결과가_존재하지_않는_검색어_테스트 { void 페이지_크기_3_첫번째_페이지_요청_테스트() { // when final Slice actual = querydslAuctionRepository.findAuctionsAllByCondition( - PageRequest.of(0, 페이지_크기_3), - 검색어_캐비어 + 검색어_캐비어, + PageRequest.of(0, 페이지_크기_3) ); // then diff --git a/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/AuctionForListSortByAuctioneerCountTest.java b/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/list/AuctionForListSortByAuctioneerCountTest.java similarity index 92% rename from backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/AuctionForListSortByAuctioneerCountTest.java rename to backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/list/AuctionForListSortByAuctioneerCountTest.java index 5faf1e672..5750e4fac 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/AuctionForListSortByAuctioneerCountTest.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/list/AuctionForListSortByAuctioneerCountTest.java @@ -1,7 +1,8 @@ -package com.ddang.ddang.auction.infrastructure.persistence; +package com.ddang.ddang.auction.infrastructure.persistence.list; import com.ddang.ddang.auction.domain.Auction; -import com.ddang.ddang.auction.infrastructure.persistence.fixture.AuctionForListSortByAuctioneerCountFixture; +import com.ddang.ddang.auction.infrastructure.persistence.QuerydslAuctionRepository; +import com.ddang.ddang.auction.infrastructure.persistence.fixture.list.AuctionForListSortByAuctioneerCountFixture; import com.ddang.ddang.configuration.JpaConfiguration; import com.ddang.ddang.configuration.QuerydslConfiguration; import com.querydsl.jpa.impl.JPAQueryFactory; @@ -26,15 +27,15 @@ class AuctionForListSortByAuctioneerCountTest extends AuctionForListSortByAuctio @BeforeEach void setUp(@Autowired final JPAQueryFactory queryFactory) { - querydslAuctionRepository = new QuerydslAuctionRepositoryImpl(queryFactory); + querydslAuctionRepository = new QuerydslAuctionRepository(queryFactory); } @Test void 페이지_크기_3_첫번째_페이지_요청_테스트() { // when final Slice actual = querydslAuctionRepository.findAuctionsAllByCondition( - PageRequest.of(0, 페이지_크기_3, 참여_인원순_정렬), - 검색어_없음 + 검색어_없음, + PageRequest.of(0, 페이지_크기_3, 참여_인원순_정렬) ); // then @@ -51,8 +52,8 @@ void setUp(@Autowired final JPAQueryFactory queryFactory) { void 페이지_크기_3_두번째_페이지_요청_테스트() { // when final Slice actual = querydslAuctionRepository.findAuctionsAllByCondition( - PageRequest.of(1, 페이지_크기_3, 참여_인원순_정렬), - 검색어_없음 + 검색어_없음, + PageRequest.of(1, 페이지_크기_3, 참여_인원순_정렬) ); // then @@ -69,8 +70,8 @@ void setUp(@Autowired final JPAQueryFactory queryFactory) { void 페이지_크기_3_세번째_페이지_요청_테스트() { // when final Slice actual = querydslAuctionRepository.findAuctionsAllByCondition( - PageRequest.of(2, 페이지_크기_3, 참여_인원순_정렬), - 검색어_없음 + 검색어_없음, + PageRequest.of(2, 페이지_크기_3, 참여_인원순_정렬) ); // then @@ -87,8 +88,8 @@ void setUp(@Autowired final JPAQueryFactory queryFactory) { void 페이지_크기_3_네번째_페이지_요청_테스트() { // when final Slice actual = querydslAuctionRepository.findAuctionsAllByCondition( - PageRequest.of(3, 페이지_크기_3, 참여_인원순_정렬), - 검색어_없음 + 검색어_없음, + PageRequest.of(3, 페이지_크기_3, 참여_인원순_정렬) ); // then @@ -105,8 +106,8 @@ void setUp(@Autowired final JPAQueryFactory queryFactory) { void 페이지_크기_3_다섯번째_페이지_요청_테스트() { // when final Slice actual = querydslAuctionRepository.findAuctionsAllByCondition( - PageRequest.of(4, 페이지_크기_3, 참여_인원순_정렬), - 검색어_없음 + 검색어_없음, + PageRequest.of(4, 페이지_크기_3, 참여_인원순_정렬) ); SoftAssertions.assertSoftly(softAssertions -> { @@ -122,8 +123,8 @@ void setUp(@Autowired final JPAQueryFactory queryFactory) { void 페이지_크기_3_여섯번째_페이지_요청_테스트() { // when final Slice actual = querydslAuctionRepository.findAuctionsAllByCondition( - PageRequest.of(5, 페이지_크기_3, 참여_인원순_정렬), - 검색어_없음 + 검색어_없음, + PageRequest.of(5, 페이지_크기_3, 참여_인원순_정렬) ); SoftAssertions.assertSoftly(softAssertions -> { @@ -137,8 +138,8 @@ void setUp(@Autowired final JPAQueryFactory queryFactory) { void 페이지_크기_3_일곱번째_페이지_요청_테스트() { // when final Slice actual = querydslAuctionRepository.findAuctionsAllByCondition( - PageRequest.of(6, 페이지_크기_3, 참여_인원순_정렬), - 검색어_없음 + 검색어_없음, + PageRequest.of(6, 페이지_크기_3, 참여_인원순_정렬) ); // then diff --git a/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/AuctionForListSortByClosingTimeTest.java b/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/list/AuctionForListSortByClosingTimeTest.java similarity index 92% rename from backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/AuctionForListSortByClosingTimeTest.java rename to backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/list/AuctionForListSortByClosingTimeTest.java index b94cac4b7..7f56c4d92 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/AuctionForListSortByClosingTimeTest.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/list/AuctionForListSortByClosingTimeTest.java @@ -1,7 +1,8 @@ -package com.ddang.ddang.auction.infrastructure.persistence; +package com.ddang.ddang.auction.infrastructure.persistence.list; import com.ddang.ddang.auction.domain.Auction; -import com.ddang.ddang.auction.infrastructure.persistence.fixture.AuctionForListSortByClosingTimeFixture; +import com.ddang.ddang.auction.infrastructure.persistence.QuerydslAuctionRepository; +import com.ddang.ddang.auction.infrastructure.persistence.fixture.list.AuctionForListSortByClosingTimeFixture; import com.ddang.ddang.configuration.JpaConfiguration; import com.ddang.ddang.configuration.QuerydslConfiguration; import com.querydsl.jpa.impl.JPAQueryFactory; @@ -26,15 +27,15 @@ class AuctionForListSortByClosingTimeTest extends AuctionForListSortByClosingTim @BeforeEach void setUp(@Autowired final JPAQueryFactory queryFactory) { - querydslAuctionRepository = new QuerydslAuctionRepositoryImpl(queryFactory); + querydslAuctionRepository = new QuerydslAuctionRepository(queryFactory); } @Test void 페이지_크기_3_첫번째_페이지_요청_테스트() { // when final Slice actual = querydslAuctionRepository.findAuctionsAllByCondition( - PageRequest.of(0, 페이지_크기_3, 마감_임박순_정렬), - 검색어_없음 + 검색어_없음, + PageRequest.of(0, 페이지_크기_3, 마감_임박순_정렬) ); // then @@ -51,8 +52,8 @@ void setUp(@Autowired final JPAQueryFactory queryFactory) { void 페이지_크기_3_두번째_페이지_요청_테스트() { // when final Slice actual = querydslAuctionRepository.findAuctionsAllByCondition( - PageRequest.of(1, 페이지_크기_3, 마감_임박순_정렬), - 검색어_없음 + 검색어_없음, + PageRequest.of(1, 페이지_크기_3, 마감_임박순_정렬) ); // then @@ -69,8 +70,8 @@ void setUp(@Autowired final JPAQueryFactory queryFactory) { void 페이지_크기_3_세번째_페이지_요청_테스트() { // when final Slice actual = querydslAuctionRepository.findAuctionsAllByCondition( - PageRequest.of(2, 페이지_크기_3, 마감_임박순_정렬), - 검색어_없음 + 검색어_없음, + PageRequest.of(2, 페이지_크기_3, 마감_임박순_정렬) ); // then @@ -87,8 +88,8 @@ void setUp(@Autowired final JPAQueryFactory queryFactory) { void 페이지_크기_3_네번째_페이지_요청_테스트() { // when final Slice actual = querydslAuctionRepository.findAuctionsAllByCondition( - PageRequest.of(3, 페이지_크기_3, 마감_임박순_정렬), - 검색어_없음 + 검색어_없음, + PageRequest.of(3, 페이지_크기_3, 마감_임박순_정렬) ); // then @@ -105,8 +106,8 @@ void setUp(@Autowired final JPAQueryFactory queryFactory) { void 페이지_크기_3_다섯번째_페이지_요청_테스트() { // when final Slice actual = querydslAuctionRepository.findAuctionsAllByCondition( - PageRequest.of(4, 페이지_크기_3, 마감_임박순_정렬), - 검색어_없음 + 검색어_없음, + PageRequest.of(4, 페이지_크기_3, 마감_임박순_정렬) ); SoftAssertions.assertSoftly(softAssertions -> { @@ -122,8 +123,8 @@ void setUp(@Autowired final JPAQueryFactory queryFactory) { void 페이지_크기_3_여섯번째_페이지_요청_테스트() { // when final Slice actual = querydslAuctionRepository.findAuctionsAllByCondition( - PageRequest.of(5, 페이지_크기_3, 마감_임박순_정렬), - 검색어_없음 + 검색어_없음, + PageRequest.of(5, 페이지_크기_3, 마감_임박순_정렬) ); SoftAssertions.assertSoftly(softAssertions -> { @@ -137,8 +138,8 @@ void setUp(@Autowired final JPAQueryFactory queryFactory) { void 페이지_크기_3_일곱번째_페이지_요청_테스트() { // when final Slice actual = querydslAuctionRepository.findAuctionsAllByCondition( - PageRequest.of(6, 페이지_크기_3, 마감_임박순_정렬), - 검색어_없음 + 검색어_없음, + PageRequest.of(6, 페이지_크기_3, 마감_임박순_정렬) ); // then diff --git a/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/AuctionForListSortByIdTest.java b/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/list/AuctionForListSortByIdTest.java similarity index 78% rename from backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/AuctionForListSortByIdTest.java rename to backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/list/AuctionForListSortByIdTest.java index 64eae857f..8024185e2 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/AuctionForListSortByIdTest.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/list/AuctionForListSortByIdTest.java @@ -1,7 +1,8 @@ -package com.ddang.ddang.auction.infrastructure.persistence; +package com.ddang.ddang.auction.infrastructure.persistence.list; import com.ddang.ddang.auction.domain.Auction; -import com.ddang.ddang.auction.infrastructure.persistence.fixture.AuctionForListSortByIdFixture; +import com.ddang.ddang.auction.infrastructure.persistence.QuerydslAuctionRepository; +import com.ddang.ddang.auction.infrastructure.persistence.fixture.list.AuctionForListSortByIdFixture; import com.ddang.ddang.configuration.JpaConfiguration; import com.ddang.ddang.configuration.QuerydslConfiguration; import com.querydsl.jpa.impl.JPAQueryFactory; @@ -26,23 +27,23 @@ class AuctionForListSortByIdTest extends AuctionForListSortByIdFixture { @BeforeEach void setUp(@Autowired final JPAQueryFactory queryFactory) { - querydslAuctionRepository = new QuerydslAuctionRepositoryImpl(queryFactory); + querydslAuctionRepository = new QuerydslAuctionRepository(queryFactory); } @Test void 페이지_크기_3_첫번째_페이지_요청_테스트() { // when final Slice actual = querydslAuctionRepository.findAuctionsAllByCondition( - PageRequest.of(0, 페이지_크기_3, id순_정렬), - 검색어_없음 + 검색어_없음, + PageRequest.of(0, 페이지_크기_3, id순_정렬) ); // then SoftAssertions.assertSoftly(softAssertions -> { softAssertions.assertThat(actual).hasSize(3); - softAssertions.assertThat(actual.getContent().get(0)).isEqualTo(첫번째_페이지_인덱스_0_id_16); - softAssertions.assertThat(actual.getContent().get(1)).isEqualTo(첫번째_페이지_인덱스_1_id_15); - softAssertions.assertThat(actual.getContent().get(2)).isEqualTo(첫번째_페이지_인덱스_2_id_14); + softAssertions.assertThat(actual.getContent().get(0)).isEqualTo(첫번째_페이지_인덱스_0_4일_후_마감_id_15); + softAssertions.assertThat(actual.getContent().get(1)).isEqualTo(첫번째_페이지_인덱스_1_4일_후_마감_id_14); + softAssertions.assertThat(actual.getContent().get(2)).isEqualTo(첫번째_페이지_인덱스_2_4일_후_마감_id_12); softAssertions.assertThat(actual.hasNext()).isTrue(); }); } @@ -51,16 +52,16 @@ void setUp(@Autowired final JPAQueryFactory queryFactory) { void 페이지_크기_3_두번째_페이지_요청_테스트() { // when final Slice actual = querydslAuctionRepository.findAuctionsAllByCondition( - PageRequest.of(1, 페이지_크기_3, id순_정렬), - 검색어_없음 + 검색어_없음, + PageRequest.of(1, 페이지_크기_3, id순_정렬) ); // then SoftAssertions.assertSoftly(softAssertions -> { softAssertions.assertThat(actual).hasSize(3); - softAssertions.assertThat(actual.getContent().get(0)).isEqualTo(두번째_페이지_인덱스_0_id_13); - softAssertions.assertThat(actual.getContent().get(1)).isEqualTo(두번째_페이지_인덱스_1_id_12); - softAssertions.assertThat(actual.getContent().get(2)).isEqualTo(두번째_페이지_인덱스_2_id_11); + softAssertions.assertThat(actual.getContent().get(0)).isEqualTo(두번째_페이지_인덱스_0_4일_후_마감_id_11); + softAssertions.assertThat(actual.getContent().get(1)).isEqualTo(두번째_페이지_인덱스_1_4일_후_마감_id_10); + softAssertions.assertThat(actual.getContent().get(2)).isEqualTo(두번째_페이지_인덱스_2_1일_후_마감_id_5); softAssertions.assertThat(actual.hasNext()).isTrue(); }); } @@ -69,16 +70,16 @@ void setUp(@Autowired final JPAQueryFactory queryFactory) { void 페이지_크기_3_세번째_페이지_요청_테스트() { // when final Slice actual = querydslAuctionRepository.findAuctionsAllByCondition( - PageRequest.of(2, 페이지_크기_3, id순_정렬), - 검색어_없음 + 검색어_없음, + PageRequest.of(2, 페이지_크기_3, id순_정렬) ); // then SoftAssertions.assertSoftly(softAssertions -> { softAssertions.assertThat(actual).hasSize(3); - softAssertions.assertThat(actual.getContent().get(0)).isEqualTo(세번째_페이지_인덱스_0_id_10); - softAssertions.assertThat(actual.getContent().get(1)).isEqualTo(세번째_페이지_인덱스_1_id_9); - softAssertions.assertThat(actual.getContent().get(2)).isEqualTo(세번째_페이지_인덱스_2_id_8); + softAssertions.assertThat(actual.getContent().get(0)).isEqualTo(세번째_페이지_인덱스_0_2일_후_마감_id_4); + softAssertions.assertThat(actual.getContent().get(1)).isEqualTo(세번째_페이지_인덱스_1_3일_후_마감_id_3); + softAssertions.assertThat(actual.getContent().get(2)).isEqualTo(세번째_페이지_인덱스_2_4일_후_마감_id_2); softAssertions.assertThat(actual.hasNext()).isTrue(); }); } @@ -87,16 +88,16 @@ void setUp(@Autowired final JPAQueryFactory queryFactory) { void 페이지_크기_3_네번째_페이지_요청_테스트() { // when final Slice actual = querydslAuctionRepository.findAuctionsAllByCondition( - PageRequest.of(3, 페이지_크기_3, id순_정렬), - 검색어_없음 + 검색어_없음, + PageRequest.of(3, 페이지_크기_3, id순_정렬) ); // then SoftAssertions.assertSoftly(softAssertions -> { softAssertions.assertThat(actual).hasSize(3); - softAssertions.assertThat(actual.getContent().get(0)).isEqualTo(네번째_페이지_인덱스_0_id_7); - softAssertions.assertThat(actual.getContent().get(1)).isEqualTo(네번째_페이지_인덱스_1_id_6); - softAssertions.assertThat(actual.getContent().get(2)).isEqualTo(네번째_페이지_인덱스_2_id_5); + softAssertions.assertThat(actual.getContent().get(0)).isEqualTo(네번째_페이지_인덱스_0_4일_전_마감_id_16); + softAssertions.assertThat(actual.getContent().get(1)).isEqualTo(네번째_페이지_인덱스_1_4일_전_마감_id_13); + softAssertions.assertThat(actual.getContent().get(2)).isEqualTo(네번째_페이지_인덱스_2_4일_전_마감_id_9); softAssertions.assertThat(actual.hasNext()).isTrue(); }); } @@ -105,15 +106,15 @@ void setUp(@Autowired final JPAQueryFactory queryFactory) { void 페이지_크기_3_다섯번째_페이지_요청_테스트() { // when final Slice actual = querydslAuctionRepository.findAuctionsAllByCondition( - PageRequest.of(4, 페이지_크기_3, id순_정렬), - 검색어_없음 + 검색어_없음, + PageRequest.of(4, 페이지_크기_3, id순_정렬) ); SoftAssertions.assertSoftly(softAssertions -> { softAssertions.assertThat(actual).hasSize(3); - softAssertions.assertThat(actual.getContent().get(0)).isEqualTo(다섯번째_페이지_인덱스_0_id_4); - softAssertions.assertThat(actual.getContent().get(1)).isEqualTo(다섯번째_페이지_인덱스_1_id_3); - softAssertions.assertThat(actual.getContent().get(2)).isEqualTo(다섯번째_페이지_인덱스_2_id_2); + softAssertions.assertThat(actual.getContent().get(0)).isEqualTo(다섯번째_페이지_인덱스_0_4일_전_마감_id_8); + softAssertions.assertThat(actual.getContent().get(1)).isEqualTo(다섯번째_페이지_인덱스_1_4일_전_마감_id_7); + softAssertions.assertThat(actual.getContent().get(2)).isEqualTo(다섯번째_페이지_인덱스_2_2일_전_마감_id_6); softAssertions.assertThat(actual.hasNext()).isTrue(); }); } @@ -122,13 +123,13 @@ void setUp(@Autowired final JPAQueryFactory queryFactory) { void 페이지_크기_3_여섯번째_페이지_요청_테스트() { // when final Slice actual = querydslAuctionRepository.findAuctionsAllByCondition( - PageRequest.of(5, 페이지_크기_3, id순_정렬), - 검색어_없음 + 검색어_없음, + PageRequest.of(5, 페이지_크기_3, id순_정렬) ); SoftAssertions.assertSoftly(softAssertions -> { softAssertions.assertThat(actual).hasSize(1); - softAssertions.assertThat(actual.getContent().get(0)).isEqualTo(여섯번째_페이지_인덱스_0_id_1); + softAssertions.assertThat(actual.getContent().get(0)).isEqualTo(여섯번째_페이지_인덱스_0_5일_전_마감_id_1); softAssertions.assertThat(actual.hasNext()).isFalse(); }); } @@ -137,8 +138,8 @@ void setUp(@Autowired final JPAQueryFactory queryFactory) { void 페이지_크기_3_일곱번째_페이지_요청_테스트() { // when final Slice actual = querydslAuctionRepository.findAuctionsAllByCondition( - PageRequest.of(6, 페이지_크기_3, id순_정렬), - 검색어_없음 + 검색어_없음, + PageRequest.of(6, 페이지_크기_3, id순_정렬) ); // then diff --git a/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/AuctionForListSortByReliabilityTest.java b/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/list/AuctionForListSortByReliabilityTest.java similarity index 92% rename from backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/AuctionForListSortByReliabilityTest.java rename to backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/list/AuctionForListSortByReliabilityTest.java index c296e536d..f711e4f77 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/AuctionForListSortByReliabilityTest.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/auction/infrastructure/persistence/list/AuctionForListSortByReliabilityTest.java @@ -1,7 +1,8 @@ -package com.ddang.ddang.auction.infrastructure.persistence; +package com.ddang.ddang.auction.infrastructure.persistence.list; import com.ddang.ddang.auction.domain.Auction; -import com.ddang.ddang.auction.infrastructure.persistence.fixture.AuctionForListSortByReliabilityFixture; +import com.ddang.ddang.auction.infrastructure.persistence.QuerydslAuctionRepository; +import com.ddang.ddang.auction.infrastructure.persistence.fixture.list.AuctionForListSortByReliabilityFixture; import com.ddang.ddang.configuration.JpaConfiguration; import com.ddang.ddang.configuration.QuerydslConfiguration; import com.querydsl.jpa.impl.JPAQueryFactory; @@ -26,15 +27,15 @@ class AuctionForListSortByReliabilityTest extends AuctionForListSortByReliabilit @BeforeEach void setUp(@Autowired final JPAQueryFactory queryFactory) { - querydslAuctionRepository = new QuerydslAuctionRepositoryImpl(queryFactory); + querydslAuctionRepository = new QuerydslAuctionRepository(queryFactory); } @Test void 페이지_크기_3_첫번째_페이지_요청_테스트() { // when final Slice actual = querydslAuctionRepository.findAuctionsAllByCondition( - PageRequest.of(0, 페이지_크기_3, 신뢰도순_정렬), - 검색어_없음 + 검색어_없음, + PageRequest.of(0, 페이지_크기_3, 신뢰도순_정렬) ); // then @@ -51,8 +52,8 @@ void setUp(@Autowired final JPAQueryFactory queryFactory) { void 페이지_크기_3_두번째_페이지_요청_테스트() { // when final Slice actual = querydslAuctionRepository.findAuctionsAllByCondition( - PageRequest.of(1, 페이지_크기_3, 신뢰도순_정렬), - 검색어_없음 + 검색어_없음, + PageRequest.of(1, 페이지_크기_3, 신뢰도순_정렬) ); // then @@ -69,8 +70,8 @@ void setUp(@Autowired final JPAQueryFactory queryFactory) { void 페이지_크기_3_세번째_페이지_요청_테스트() { // when final Slice actual = querydslAuctionRepository.findAuctionsAllByCondition( - PageRequest.of(2, 페이지_크기_3, 신뢰도순_정렬), - 검색어_없음 + 검색어_없음, + PageRequest.of(2, 페이지_크기_3, 신뢰도순_정렬) ); // then @@ -87,8 +88,8 @@ void setUp(@Autowired final JPAQueryFactory queryFactory) { void 페이지_크기_3_네번째_페이지_요청_테스트() { // when final Slice actual = querydslAuctionRepository.findAuctionsAllByCondition( - PageRequest.of(3, 페이지_크기_3, 신뢰도순_정렬), - 검색어_없음 + 검색어_없음, + PageRequest.of(3, 페이지_크기_3, 신뢰도순_정렬) ); // then @@ -105,8 +106,8 @@ void setUp(@Autowired final JPAQueryFactory queryFactory) { void 페이지_크기_3_다섯번째_페이지_요청_테스트() { // when final Slice actual = querydslAuctionRepository.findAuctionsAllByCondition( - PageRequest.of(4, 페이지_크기_3, 신뢰도순_정렬), - 검색어_없음 + 검색어_없음, + PageRequest.of(4, 페이지_크기_3, 신뢰도순_정렬) ); SoftAssertions.assertSoftly(softAssertions -> { @@ -122,8 +123,8 @@ void setUp(@Autowired final JPAQueryFactory queryFactory) { void 페이지_크기_3_여섯번째_페이지_요청_테스트() { // when final Slice actual = querydslAuctionRepository.findAuctionsAllByCondition( - PageRequest.of(5, 페이지_크기_3, 신뢰도순_정렬), - 검색어_없음 + 검색어_없음, + PageRequest.of(5, 페이지_크기_3, 신뢰도순_정렬) ); SoftAssertions.assertSoftly(softAssertions -> { @@ -137,8 +138,8 @@ void setUp(@Autowired final JPAQueryFactory queryFactory) { void 페이지_크기_3_일곱번째_페이지_요청_테스트() { // when final Slice actual = querydslAuctionRepository.findAuctionsAllByCondition( - PageRequest.of(6, 페이지_크기_3, 신뢰도순_정렬), - 검색어_없음 + 검색어_없음, + PageRequest.of(6, 페이지_크기_3, 신뢰도순_정렬) ); // then diff --git a/backend/ddang/src/test/java/com/ddang/ddang/auction/presentation/AuctionControllerTest.java b/backend/ddang/src/test/java/com/ddang/ddang/auction/presentation/AuctionControllerTest.java index cc415afe8..56b5b9669 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/auction/presentation/AuctionControllerTest.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/auction/presentation/AuctionControllerTest.java @@ -266,13 +266,17 @@ void setUp() { jsonPath("$.auction.bidUnit", is(경매_조회_dto.bidUnit())), jsonPath("$.auction.registerTime").exists(), jsonPath("$.auction.closingTime").exists(), - jsonPath("$.auction.directRegions[0].first", is(경매_조회_dto.auctionRegions().get(0).firstRegionDto().regionName())), - jsonPath("$.auction.directRegions[0].second", is(경매_조회_dto.auctionRegions().get(0).secondRegionDto().regionName())), - jsonPath("$.auction.directRegions[0].third", is(경매_조회_dto.auctionRegions().get(0).thirdRegionDto().regionName())), + jsonPath("$.auction.directRegions[0].first", + is(경매_조회_dto.auctionRegions().get(0).firstRegionDto().regionName())), + jsonPath("$.auction.directRegions[0].second", + is(경매_조회_dto.auctionRegions().get(0).secondRegionDto().regionName())), + jsonPath("$.auction.directRegions[0].third", + is(경매_조회_dto.auctionRegions().get(0).thirdRegionDto().regionName())), jsonPath("$.auction.auctioneerCount", is(경매_조회_dto.auctioneerCount())), jsonPath("$.chat.id").exists(), jsonPath("$.chat.isChatParticipant", is(true)), jsonPath("$.isOwner", is(true)), + jsonPath("$.isLastBidder", is(false)), jsonPath("$.seller.nickname", is(경매_조회_dto.sellerName())), jsonPath("$.seller.id", is(경매_조회_dto.sellerId()), Long.class), jsonPath("$.seller.image", containsString(프로필_이미지_상대_주소)) @@ -313,18 +317,26 @@ void setUp() { ) .andExpectAll( status().isOk(), - jsonPath("$.auctions.[0].id", is(두번째_경매_조회_dto.id()), Long.class), + jsonPath("$.auctions.[0].id", is(두번째_경매_조회_dto.id()), + Long.class), jsonPath("$.auctions.[0].title", is(두번째_경매_조회_dto.title())), - jsonPath("$.auctions.[0].image", containsString(경매_이미지_상대_주소)), - jsonPath("$.auctions.[0].auctionPrice", is(두번째_경매_조회_dto.startPrice())), + jsonPath("$.auctions.[0].image", + containsString(경매_이미지_상대_주소)), + jsonPath("$.auctions.[0].auctionPrice", + is(두번째_경매_조회_dto.startPrice())), jsonPath("$.auctions.[0].status").exists(), - jsonPath("$.auctions.[0].auctioneerCount", is(두번째_경매_조회_dto.auctioneerCount())), - jsonPath("$.auctions.[1].id", is(첫번째_경매_조회_dto.id()), Long.class), + jsonPath("$.auctions.[0].auctioneerCount", + is(두번째_경매_조회_dto.auctioneerCount())), + jsonPath("$.auctions.[1].id", is(첫번째_경매_조회_dto.id()), + Long.class), jsonPath("$.auctions.[1].title", is(첫번째_경매_조회_dto.title())), - jsonPath("$.auctions.[1].image", containsString(경매_이미지_상대_주소)), - jsonPath("$.auctions.[1].auctionPrice", is(첫번째_경매_조회_dto.startPrice())), + jsonPath("$.auctions.[1].image", + containsString(경매_이미지_상대_주소)), + jsonPath("$.auctions.[1].auctionPrice", + is(첫번째_경매_조회_dto.startPrice())), jsonPath("$.auctions.[1].status").exists(), - jsonPath("$.auctions.[1].auctioneerCount", is(첫번째_경매_조회_dto.auctioneerCount())), + jsonPath("$.auctions.[1].auctioneerCount", + is(첫번째_경매_조회_dto.auctioneerCount())), jsonPath("$.isLast").exists() ); @@ -410,7 +422,7 @@ void setUp() { resultActions.andDo( restDocs.document( requestHeaders( - headerWithName("Authorization").description("회원 Bearer 인증 정보") + headerWithName("Authorization").description("사용자 Bearer 인증 정보") ), pathParameters( parameterWithName("auctionId").description("조회하고자 하는 경매 ID") @@ -458,9 +470,12 @@ void setUp() { .description("판매자 신뢰도"), fieldWithPath("chat.id").type(JsonFieldType.NUMBER).description("채팅방 ID"), fieldWithPath("chat.isChatParticipant").type(JsonFieldType.BOOLEAN) - .description("채팅방을 생성 가능 유저 여부"), + .description( + "로그인한 사용자가 채팅방 생성이 가능한 사용자인지에 대한 여부"), fieldWithPath("isOwner").type(JsonFieldType.BOOLEAN) - .description("유저가 해당 경매 글을 작성한 유저인지에 대한 여부") + .description("로그인한 사용자가 해당 경매 글을 작성한 사용자인지에 대한 여부"), + fieldWithPath("isLastBidder").type(JsonFieldType.BOOLEAN) + .description("로그인한 사용자가 해당 경매의 최고 입찰자인지에 대한 여부") ) ) ); diff --git a/backend/ddang/src/test/java/com/ddang/ddang/auction/presentation/AuctionQnaControllerTest.java b/backend/ddang/src/test/java/com/ddang/ddang/auction/presentation/AuctionQnaControllerTest.java index ca0ab7c8c..0ed09b1ef 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/auction/presentation/AuctionQnaControllerTest.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/auction/presentation/AuctionQnaControllerTest.java @@ -67,7 +67,7 @@ void setUp() { void 경매_아이디를_통해_질문과_답변을_모두_조회한다() throws Exception { // given given(tokenDecoder.decode(eq(TokenType.ACCESS), anyString())).willReturn(Optional.of(사용자_ID_클레임)); - given(questionService.readAllByAuctionId(anyLong())).willReturn(질문과_답변_정보들_dto); + given(questionService.readAllByAuctionId(anyLong(), anyLong())).willReturn(질문과_답변_정보들_dto); // when & then final ResultActions resultActions = @@ -83,6 +83,7 @@ void setUp() { jsonPath("$.qnas.[0].question.writer.image").exists(), jsonPath("$.qnas.[0].question.createdTime").exists(), jsonPath("$.qnas.[0].question.content", is(질문_정보_dto1.content())), + jsonPath("$.qnas.[0].question.isQuestioner", is(질문_정보_dto1.isQuestioner()), Boolean.class), jsonPath("$.qnas.[0].answer.id", is(답변_정보_dto1.id()), Long.class), jsonPath("$.qnas.[0].answer.writer.id", is(판매자_정보_dto.id()), Long.class), jsonPath("$.qnas.[0].answer.writer.name", is(판매자_정보_dto.name())), @@ -95,6 +96,7 @@ void setUp() { jsonPath("$.qnas.[1].question.writer.image").exists(), jsonPath("$.qnas.[1].question.createdTime").exists(), jsonPath("$.qnas.[1].question.content", is(질문_정보_dto2.content())), + jsonPath("$.qnas.[1].question.isQuestioner", is(질문_정보_dto2.isQuestioner()), Boolean.class), jsonPath("$.qnas.[1].answer.id", is(답변_정보_dto2.id()), Long.class), jsonPath("$.qnas.[1].answer.writer.id", is(판매자_정보_dto.id()), Long.class), jsonPath("$.qnas.[1].answer.writer.name", is(판매자_정보_dto.name())), @@ -110,7 +112,7 @@ void setUp() { void 존재하지_않는_경매_아이디를_통해_질문과_답변을_모두_조회할시_404를_반환한다() throws Exception { // given given(tokenDecoder.decode(eq(TokenType.ACCESS), anyString())).willReturn(Optional.of(사용자_ID_클레임)); - given(questionService.readAllByAuctionId(anyLong())) + given(questionService.readAllByAuctionId(anyLong(), anyLong())) .willThrow(new AuctionNotFoundException("해당 경매를 찾을 수 없습니다.")); // when & then @@ -146,6 +148,8 @@ void setUp() { .description("질문 등록 시간"), fieldWithPath("qnas.[].question.content").type(JsonFieldType.STRING) .description("질문 내용"), + fieldWithPath("qnas.[].question.isQuestioner").type(JsonFieldType.BOOLEAN) + .description("질문 작성자 여부 확인"), fieldWithPath("qnas.[].answer").type(JsonFieldType.OBJECT) .description("답변 정보 JSON"), fieldWithPath("qnas.[].answer.id").type(JsonFieldType.NUMBER) diff --git a/backend/ddang/src/test/java/com/ddang/ddang/auction/presentation/fixture/AuctionControllerFixture.java b/backend/ddang/src/test/java/com/ddang/ddang/auction/presentation/fixture/AuctionControllerFixture.java index 804f0e3ec..b92c77e17 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/auction/presentation/fixture/AuctionControllerFixture.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/auction/presentation/fixture/AuctionControllerFixture.java @@ -157,7 +157,8 @@ void fixtureSetUp() throws JsonProcessingException { "판매자", 3.5d, false, - AuctionStatus.UNBIDDEN + AuctionStatus.UNBIDDEN, + null ); 첫번째_경매_조회_dto = new ReadAuctionDto( @@ -180,7 +181,8 @@ void fixtureSetUp() throws JsonProcessingException { "판매자", 3.5d, false, - AuctionStatus.UNBIDDEN + AuctionStatus.UNBIDDEN, + null ); 두번째_경매_조회_dto = new ReadAuctionDto( @@ -203,7 +205,8 @@ void fixtureSetUp() throws JsonProcessingException { "판매자", 3.5d, false, - AuctionStatus.UNBIDDEN + AuctionStatus.UNBIDDEN, + null ); 경매_목록_조회_dto = new ReadAuctionsDto(List.of(두번째_경매_조회_dto, 첫번째_경매_조회_dto), true); diff --git a/backend/ddang/src/test/java/com/ddang/ddang/auction/presentation/fixture/AuctionQuestionControllerFixture.java b/backend/ddang/src/test/java/com/ddang/ddang/auction/presentation/fixture/AuctionQuestionControllerFixture.java index 2ee6bd63c..588ae7036 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/auction/presentation/fixture/AuctionQuestionControllerFixture.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/auction/presentation/fixture/AuctionQuestionControllerFixture.java @@ -34,10 +34,10 @@ public class AuctionQuestionControllerFixture extends CommonControllerSliceTest "12346", false ); - protected ReadQuestionDto 질문_정보_dto1 = new ReadQuestionDto(1L, 질문자_정보_dto, "질문1", LocalDateTime.now()); - protected ReadQuestionDto 질문_정보_dto2 = new ReadQuestionDto(2L, 질문자_정보_dto, "질문2", LocalDateTime.now()); - protected ReadAnswerDto 답변_정보_dto1 = new ReadAnswerDto(1L, 판매자_정보_dto, "답변1", LocalDateTime.now()); - protected ReadAnswerDto 답변_정보_dto2 = new ReadAnswerDto(2L, 판매자_정보_dto, "답변1", LocalDateTime.now()); + protected ReadQuestionDto 질문_정보_dto1 = new ReadQuestionDto(1L, 질문자_정보_dto, "질문1", LocalDateTime.now(), false, false); + protected ReadQuestionDto 질문_정보_dto2 = new ReadQuestionDto(2L, 질문자_정보_dto, "질문2", LocalDateTime.now(), false, false); + protected ReadAnswerDto 답변_정보_dto1 = new ReadAnswerDto(1L, 판매자_정보_dto, "답변1", LocalDateTime.now(), false); + protected ReadAnswerDto 답변_정보_dto2 = new ReadAnswerDto(2L, 판매자_정보_dto, "답변1", LocalDateTime.now(), false); private ReadQnaDto 질문과_답변_정보_dto1 = new ReadQnaDto(질문_정보_dto1, 답변_정보_dto1); private ReadQnaDto 질문과_답변_정보_dto2 = new ReadQnaDto(질문_정보_dto2, 답변_정보_dto2); protected ReadQnasDto 질문과_답변_정보들_dto = diff --git a/backend/ddang/src/test/java/com/ddang/ddang/authentication/application/AuthenticationServiceTest.java b/backend/ddang/src/test/java/com/ddang/ddang/authentication/application/AuthenticationServiceTest.java index 6238cad68..82757e984 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/authentication/application/AuthenticationServiceTest.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/authentication/application/AuthenticationServiceTest.java @@ -1,12 +1,10 @@ package com.ddang.ddang.authentication.application; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.BDDMockito.given; - +import com.ddang.ddang.auction.domain.repository.AuctionRepository; +import com.ddang.ddang.authentication.application.dto.LoginInformationDto; import com.ddang.ddang.authentication.application.dto.TokenDto; import com.ddang.ddang.authentication.application.exception.InvalidWithdrawalException; +import com.ddang.ddang.authentication.application.exception.WithdrawalNotAllowedException; import com.ddang.ddang.authentication.application.fixture.AuthenticationServiceFixture; import com.ddang.ddang.authentication.domain.Oauth2UserInformationProviderComposite; import com.ddang.ddang.authentication.domain.TokenDecoder; @@ -16,27 +14,26 @@ import com.ddang.ddang.authentication.infrastructure.oauth2.OAuth2UserInformationProvider; import com.ddang.ddang.configuration.IsolateDatabase; import com.ddang.ddang.device.application.DeviceTokenService; -import com.ddang.ddang.device.infrastructure.persistence.JpaDeviceTokenRepository; -import com.ddang.ddang.image.application.exception.ImageNotFoundException; -import com.ddang.ddang.image.infrastructure.persistence.JpaProfileImageRepository; -import com.ddang.ddang.user.infrastructure.persistence.JpaUserRepository; +import com.ddang.ddang.device.domain.repository.DeviceTokenRepository; +import com.ddang.ddang.user.domain.repository.UserRepository; import org.assertj.core.api.SoftAssertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayNameGeneration; import org.junit.jupiter.api.DisplayNameGenerator; import org.junit.jupiter.api.Test; -import org.mockito.Mock; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.mock.mockito.MockBean; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.BDDMockito.given; + @IsolateDatabase @DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) @SuppressWarnings("NonAsciiCharacters") class AuthenticationServiceTest extends AuthenticationServiceFixture { - @Mock - JpaProfileImageRepository defaultProfileImageRepository; - @MockBean Oauth2UserInformationProviderComposite providerComposite; @@ -47,10 +44,10 @@ class AuthenticationServiceTest extends AuthenticationServiceFixture { DeviceTokenService deviceTokenService; @Autowired - JpaUserRepository userRepository; + UserRepository userRepository; @Autowired - JpaProfileImageRepository profileImageRepository; + AuctionRepository auctionRepository; @Autowired TokenEncoder tokenEncoder; @@ -62,28 +59,17 @@ class AuthenticationServiceTest extends AuthenticationServiceFixture { BlackListTokenService blackListTokenService; @Autowired - JpaDeviceTokenRepository deviceTokenRepository; + DeviceTokenRepository deviceTokenRepository; AuthenticationService authenticationService; - AuthenticationService profileImageNotFoundAuthenticationService; @BeforeEach - void setUp() { + void fixtureSetUp() { authenticationService = new AuthenticationService( deviceTokenService, providerComposite, userRepository, - profileImageRepository, - tokenEncoder, - tokenDecoder, - blackListTokenService, - deviceTokenRepository - ); - profileImageNotFoundAuthenticationService = new AuthenticationService( - deviceTokenService, - providerComposite, - userRepository, - defaultProfileImageRepository, + auctionRepository, tokenEncoder, tokenDecoder, blackListTokenService, @@ -91,6 +77,23 @@ void setUp() { ); } + @Test + void 로그인할_때_가입하지_않은_사용자라면_회원가입을_진행한다() { + // given + given(providerComposite.findProvider(지원하는_소셜_로그인_타입)).willReturn(userInfoProvider); + given(userInfoProvider.findUserInformation(anyString())).willReturn(가입하지_않은_사용자_회원_정보); + + // when + final LoginInformationDto actual = authenticationService.login(지원하는_소셜_로그인_타입, 유효한_소셜_로그인_토큰, 디바이스_토큰); + + + // then + SoftAssertions.assertSoftly(softAssertions -> { + softAssertions.assertThat(actual.tokenDto().accessToken()).isNotEmpty().contains("Bearer "); + softAssertions.assertThat(actual.tokenDto().refreshToken()).isNotEmpty().contains("Bearer "); + }); + } + @Test void 지원하는_소셜_로그인_기능이_아닌_경우_예외가_발생한다() { // given @@ -119,43 +122,33 @@ void setUp() { void 가입한_회원이_소셜_로그인을_할_경우_accessToken과_refreshToken을_반환한다() { // given given(providerComposite.findProvider(지원하는_소셜_로그인_타입)).willReturn(userInfoProvider); - given(userInfoProvider.findUserInformation(anyString())).willReturn(사용자_회원_정보); + given(userInfoProvider.findUserInformation(anyString())).willReturn(가입한_사용자_회원_정보); // when - final TokenDto actual = authenticationService.login(지원하는_소셜_로그인_타입, 유효한_소셜_로그인_토큰, 디바이스_토큰); + final LoginInformationDto actual = authenticationService.login(지원하는_소셜_로그인_타입, 유효한_소셜_로그인_토큰, 디바이스_토큰); // then SoftAssertions.assertSoftly(softAssertions -> { - softAssertions.assertThat(actual.accessToken()).isNotEmpty().contains("Bearer "); - softAssertions.assertThat(actual.refreshToken()).isNotEmpty().contains("Bearer "); + softAssertions.assertThat(actual.tokenDto().accessToken()).isNotEmpty().contains("Bearer "); + softAssertions.assertThat(actual.tokenDto().refreshToken()).isNotEmpty().contains("Bearer "); + softAssertions.assertThat(actual.isSignUpUser()).isFalse(); }); } @Test - void 가입하지_않은_회원이_소셜_로그인을_할_때_기본_프로필_이미지를_찾을_수_없으면_예외가_발생한다() { + void 가입하지_않은_사용자가_소셜_로그인을_할_경우_accessToken과_refreshToken을_반환한다() { // given given(providerComposite.findProvider(지원하는_소셜_로그인_타입)).willReturn(userInfoProvider); given(userInfoProvider.findUserInformation(anyString())).willReturn(가입하지_않은_사용자_회원_정보); - // when & then - assertThatThrownBy(() -> profileImageNotFoundAuthenticationService.login(지원하는_소셜_로그인_타입, 유효한_소셜_로그인_토큰, 디바이스_토큰)) - .isInstanceOf(ImageNotFoundException.class) - .hasMessage("기본 이미지를 찾을 수 없습니다."); - } - - @Test - void 가입하지_않은_회원이_소셜_로그인을_할_경우_accessToken과_refreshToken을_반환한다() { - // given - given(providerComposite.findProvider(지원하는_소셜_로그인_타입)).willReturn(userInfoProvider); - given(userInfoProvider.findUserInformation(anyString())).willReturn(사용자_회원_정보); - // when - final TokenDto actual = authenticationService.login(지원하는_소셜_로그인_타입, 유효한_소셜_로그인_토큰, 디바이스_토큰); + final LoginInformationDto actual = authenticationService.login(지원하는_소셜_로그인_타입, 유효한_소셜_로그인_토큰, 디바이스_토큰); // then SoftAssertions.assertSoftly(softAssertions -> { - softAssertions.assertThat(actual.accessToken()).isNotEmpty(); - softAssertions.assertThat(actual.refreshToken()).isNotEmpty(); + softAssertions.assertThat(actual.tokenDto().accessToken()).isNotEmpty(); + softAssertions.assertThat(actual.tokenDto().refreshToken()).isNotEmpty(); + softAssertions.assertThat(actual.isSignUpUser()).isTrue(); }); } @@ -163,15 +156,16 @@ void setUp() { void 탈퇴한_회원이_소셜_로그인을_할_경우_accessToken과_refreshToken을_반환한다() { // given given(providerComposite.findProvider(지원하는_소셜_로그인_타입)).willReturn(userInfoProvider); - given(userInfoProvider.findUserInformation(anyString())).willReturn(사용자_회원_정보); + given(userInfoProvider.findUserInformation(anyString())).willReturn(가입한_사용자_회원_정보); // when - final TokenDto actual = authenticationService.login(지원하는_소셜_로그인_타입, 유효한_소셜_로그인_토큰, 디바이스_토큰); + final LoginInformationDto actual = authenticationService.login(지원하는_소셜_로그인_타입, 유효한_소셜_로그인_토큰, 디바이스_토큰); // then SoftAssertions.assertSoftly(softAssertions -> { - softAssertions.assertThat(actual.accessToken()).isNotEmpty(); - softAssertions.assertThat(actual.refreshToken()).isNotEmpty(); + softAssertions.assertThat(actual.tokenDto().accessToken()).isNotEmpty(); + softAssertions.assertThat(actual.tokenDto().refreshToken()).isNotEmpty(); + softAssertions.assertThat(actual.isSignUpUser()).isFalse(); }); } @@ -225,13 +219,17 @@ void setUp() { void 가입한_회원이_탈퇴하는_경우_정상처리한다() throws InvalidWithdrawalException { // given given(providerComposite.findProvider(지원하는_소셜_로그인_타입)).willReturn(userInfoProvider); - given(userInfoProvider.findUserInformation(anyString())).willReturn(사용자_회원_정보); + given(userInfoProvider.findUserInformation(anyString())).willReturn(가입한_사용자_회원_정보); // when - authenticationService.withdrawal(지원하는_소셜_로그인_타입, 유효한_액세스_토큰, 유효한_리프레시_토큰); + authenticationService.withdrawal(유효한_액세스_토큰, 유효한_리프레시_토큰); // then - assertThat(사용자.isDeleted()).isTrue(); + SoftAssertions.assertSoftly(softAssertions -> { + softAssertions.assertThat(사용자.isDeleted()).isTrue(); + softAssertions.assertThat(사용자.getName()).isNotEqualTo(사용자_이름); + softAssertions.assertThat(사용자.getProfileImage()).isNull(); + }); } @Test @@ -241,9 +239,9 @@ void setUp() { given(userInfoProvider.unlinkUserBy(anyString())).willReturn(탈퇴한_사용자_회원_정보); // when && then - assertThatThrownBy(() -> authenticationService.withdrawal(지원하는_소셜_로그인_타입, 탈퇴한_사용자_액세스_토큰, 유효한_리프레시_토큰)) + assertThatThrownBy(() -> authenticationService.withdrawal(탈퇴한_사용자_액세스_토큰, 유효한_리프레시_토큰)) .isInstanceOf(InvalidWithdrawalException.class) - .hasMessage("탈퇴에 대한 권한 없습니다."); + .hasMessage("탈퇴에 대한 권한이 없습니다."); } @Test @@ -253,33 +251,40 @@ void setUp() { given(userInfoProvider.findUserInformation(anyString())).willThrow(new InvalidTokenException("401 Unauthorized")); // when & then - assertThatThrownBy(() -> authenticationService.withdrawal(지원하는_소셜_로그인_타입, 존재하지_않는_사용자_액세스_토큰, 유효한_리프레시_토큰)) + assertThatThrownBy(() -> authenticationService.withdrawal(존재하지_않는_사용자_액세스_토큰, 유효한_리프레시_토큰)) .isInstanceOf(InvalidWithdrawalException.class) - .hasMessage("탈퇴에 대한 권한 없습니다."); + .hasMessage("탈퇴에 대한 권한이 없습니다."); } @Test void 탈퇴할_때_유효한_토큰이_아닌_경우_예외가_발생한다() { // when & then - assertThatThrownBy(() -> authenticationService.withdrawal(지원하는_소셜_로그인_타입, 유효하지_않은_액세스_토큰, 유효한_리프레시_토큰)) + assertThatThrownBy(() -> authenticationService.withdrawal(유효하지_않은_액세스_토큰, 유효한_리프레시_토큰)) .isInstanceOf(InvalidTokenException.class) .hasMessage("유효한 토큰이 아닙니다."); } @Test - void 로그인할_때_가입하지_않은_사용자라면_회원가입을_진행한다() { + void 탈퇴할_때_등록한_경매중_진행중인_경매가_있다면_예외가_발생한다() { // given given(providerComposite.findProvider(지원하는_소셜_로그인_타입)).willReturn(userInfoProvider); - given(userInfoProvider.findUserInformation(anyString())).willReturn(가입하지_않은_사용자_회원_정보); + given(userInfoProvider.findUserInformation(anyString())).willReturn(현재_진행중인_경매가_있는_사용자_회원_정보); - // when - final TokenDto actual = authenticationService.login(지원하는_소셜_로그인_타입, 유효한_소셜_로그인_토큰, 디바이스_토큰); + // when & then + assertThatThrownBy(() -> authenticationService.withdrawal(현재_진행중인_경매가_있는_사용자_액세스_토큰, 현재_진행중인_경매가_있는_사용자_리프래시_토큰)) + .isInstanceOf(WithdrawalNotAllowedException.class) + .hasMessage("등록한 경매 중 현재 진행 중인 것이 있기에 탈퇴할 수 없습니다."); + } + @Test + void 탈퇴할_때_등록한_경매중_진행중인_경매의_마지막_입찰자라면_예외가_발생한다() { + // given + given(providerComposite.findProvider(지원하는_소셜_로그인_타입)).willReturn(userInfoProvider); + given(userInfoProvider.findUserInformation(anyString())).willReturn(현재_진행중인_경매가_있는_사용자_회원_정보); - // then - SoftAssertions.assertSoftly(softAssertions -> { - softAssertions.assertThat(actual.accessToken()).isNotEmpty().contains("Bearer "); - softAssertions.assertThat(actual.refreshToken()).isNotEmpty().contains("Bearer "); - }); + // when & then + assertThatThrownBy(() -> authenticationService.withdrawal(현재_진행중인_경매의_마지막_입찰자인_사용자_액세스_토큰, 현재_진행중인_경매의_마지막_입찰자인_사용자_리프래시_토큰)) + .isInstanceOf(WithdrawalNotAllowedException.class) + .hasMessage("마지막 입찰자로 등록되어 있는 것이 있기에 탈퇴할 수 없습니다."); } } diff --git a/backend/ddang/src/test/java/com/ddang/ddang/authentication/application/fixture/AuthenticationServiceFixture.java b/backend/ddang/src/test/java/com/ddang/ddang/authentication/application/fixture/AuthenticationServiceFixture.java index 19ef0f791..579e50762 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/authentication/application/fixture/AuthenticationServiceFixture.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/authentication/application/fixture/AuthenticationServiceFixture.java @@ -1,16 +1,26 @@ package com.ddang.ddang.authentication.application.fixture; +import com.ddang.ddang.auction.domain.Auction; +import com.ddang.ddang.auction.domain.BidUnit; +import com.ddang.ddang.auction.domain.Price; +import com.ddang.ddang.auction.domain.repository.AuctionRepository; import com.ddang.ddang.authentication.domain.TokenEncoder; import com.ddang.ddang.authentication.domain.TokenType; import com.ddang.ddang.authentication.domain.dto.UserInformationDto; +import com.ddang.ddang.authentication.infrastructure.jwt.PrivateClaims; import com.ddang.ddang.authentication.infrastructure.oauth2.Oauth2Type; +import com.ddang.ddang.bid.domain.Bid; +import com.ddang.ddang.bid.domain.BidPrice; +import com.ddang.ddang.bid.domain.repository.BidRepository; +import com.ddang.ddang.bid.infrastructure.persistence.BidRepositoryImpl; +import com.ddang.ddang.bid.infrastructure.persistence.JpaBidRepository; import com.ddang.ddang.device.domain.DeviceToken; -import com.ddang.ddang.device.infrastructure.persistence.JpaDeviceTokenRepository; +import com.ddang.ddang.device.domain.repository.DeviceTokenRepository; import com.ddang.ddang.image.domain.ProfileImage; import com.ddang.ddang.image.infrastructure.persistence.JpaProfileImageRepository; import com.ddang.ddang.user.domain.Reliability; import com.ddang.ddang.user.domain.User; -import com.ddang.ddang.user.infrastructure.persistence.JpaUserRepository; +import com.ddang.ddang.user.domain.repository.UserRepository; import org.junit.jupiter.api.BeforeEach; import org.springframework.beans.factory.annotation.Autowired; @@ -30,12 +40,17 @@ public class AuthenticationServiceFixture { protected String 디바이스_토큰 = "deviceToken"; + protected String 사용자_이름; protected User 사용자; protected User 탈퇴한_사용자; - protected UserInformationDto 사용자_회원_정보 = new UserInformationDto(12345L); + protected PrivateClaims 사용자_id_클레임 = new PrivateClaims(1L); + + protected UserInformationDto 가입한_사용자_회원_정보 = new UserInformationDto(12345L); protected UserInformationDto 탈퇴한_사용자_회원_정보 = new UserInformationDto(54321L); protected UserInformationDto 가입하지_않은_사용자_회원_정보 = new UserInformationDto(-99999L); + protected UserInformationDto 현재_진행중인_경매가_있는_사용자_회원_정보; + protected UserInformationDto 현재_진행중인_경매의_마지막_입찰자인_사용자_회원_정보; protected String 유효한_액세스_토큰; protected String 유효하지_않은_액세스_토큰 = "Bearer invalidAccessToken"; @@ -45,21 +60,32 @@ public class AuthenticationServiceFixture { protected String 유효한_리프레시_토큰; protected String 만료된_리프레시_토큰; protected String 유효하지_않은_타입의_리프레시_토큰 = "invalidRefreshToken"; + protected String 현재_진행중인_경매가_있는_사용자_액세스_토큰; + protected String 현재_진행중인_경매가_있는_사용자_리프래시_토큰; + protected String 현재_진행중인_경매의_마지막_입찰자인_사용자_액세스_토큰; + protected String 현재_진행중인_경매의_마지막_입찰자인_사용자_리프래시_토큰; @Autowired - private JpaUserRepository userRepository; + private UserRepository userRepository; @Autowired private JpaProfileImageRepository profileImageRepository; + @Autowired + private AuctionRepository auctionRepository; + @Autowired private TokenEncoder tokenEncoder; @Autowired - private JpaDeviceTokenRepository deviceTokenRepository; + private DeviceTokenRepository deviceTokenRepository; + + private BidRepository bidRepository; @BeforeEach - void fixtureSetUp() { + void fixtureSetUp(@Autowired final JpaBidRepository jpaBidRepository) { + bidRepository = new BidRepositoryImpl(jpaBidRepository); + profileImageRepository.save(new ProfileImage("default_profile_image.png", "default_profile_image.png")); 사용자 = User.builder() @@ -67,22 +93,55 @@ void fixtureSetUp() { .profileImage(new ProfileImage("upload.png", "store.png")) .reliability(new Reliability(0.0d)) .oauthId("12345") + .oauth2Type(Oauth2Type.KAKAO) .build(); + 사용자_이름 = 사용자.getName(); 탈퇴한_사용자 = User.builder() .name("kakao12346") .profileImage(new ProfileImage("upload.png", "store.png")) .reliability(new Reliability(0.0d)) .oauthId("12346") + .oauth2Type(Oauth2Type.KAKAO) .build(); + final User 현재_진행중인_경매가_있는_사용자 = User.builder() + .name("kakao12347") + .profileImage(new ProfileImage("upload.png", "store.png")) + .reliability(new Reliability(0.0d)) + .oauthId("12347") + .oauth2Type(Oauth2Type.KAKAO) + .build(); + final User 현재_진행중인_경매의_마지막_입찰자인_사용자 = User.builder() + .name("kakao12348") + .profileImage(new ProfileImage("upload.png", "store.png")) + .reliability(new Reliability(0.0d)) + .oauthId("12348") + .oauth2Type(Oauth2Type.KAKAO) + .build(); userRepository.save(사용자); 탈퇴한_사용자.withdrawal(); userRepository.save(탈퇴한_사용자); + userRepository.save(현재_진행중인_경매가_있는_사용자); + userRepository.save(현재_진행중인_경매의_마지막_입찰자인_사용자); final DeviceToken deviceToken = new DeviceToken(사용자, 디바이스_토큰); deviceTokenRepository.save(deviceToken); + final Auction 진행중인_경매 = Auction.builder() + .seller(현재_진행중인_경매가_있는_사용자) + .title("경매 상품") + .description("이것은 경매 상품입니다.") + .bidUnit(new BidUnit(1_000)) + .startPrice(new Price(1_000)) + .closingTime(LocalDateTime.now().plusDays(7)) + .build(); + final Auction save1 = auctionRepository.save(진행중인_경매); + + final Bid 진행중인_경매의_마지막_입찰 = new Bid(진행중인_경매, 현재_진행중인_경매의_마지막_입찰자인_사용자, new BidPrice(10_000)); + 진행중인_경매.updateLastBid(진행중인_경매의_마지막_입찰); + final Bid save = bidRepository.save(진행중인_경매의_마지막_입찰); + 유효한_리프레시_토큰 = tokenEncoder.encode( LocalDateTime.now(), TokenType.REFRESH, @@ -90,7 +149,7 @@ void fixtureSetUp() { ); 만료된_리프레시_토큰 = tokenEncoder.encode( - LocalDateTime.ofInstant(Instant.parse("2023-01-01T22:21:20Z"), ZoneId.of("UTC")), + LocalDateTime.ofInstant(Instant.parse("2023-01-01T22:21:20Z"), ZoneId.of("UTC")), TokenType.REFRESH, Map.of("userId", 1L) ); @@ -124,5 +183,28 @@ void fixtureSetUp() { TokenType.ACCESS, Map.of("userId", -99999L) ); + + 현재_진행중인_경매가_있는_사용자_회원_정보 = new UserInformationDto(현재_진행중인_경매가_있는_사용자.getId()); + 현재_진행중인_경매의_마지막_입찰자인_사용자_회원_정보 = new UserInformationDto(현재_진행중인_경매의_마지막_입찰자인_사용자.getId()); + 현재_진행중인_경매가_있는_사용자_액세스_토큰 = tokenEncoder.encode( + LocalDateTime.now(), + TokenType.ACCESS, + Map.of("userId", 현재_진행중인_경매가_있는_사용자.getId()) + ); + 현재_진행중인_경매가_있는_사용자_리프래시_토큰 = tokenEncoder.encode( + LocalDateTime.now(), + TokenType.REFRESH, + Map.of("userId", 현재_진행중인_경매가_있는_사용자.getId()) + ); + 현재_진행중인_경매의_마지막_입찰자인_사용자_액세스_토큰 = tokenEncoder.encode( + LocalDateTime.now(), + TokenType.ACCESS, + Map.of("userId", 현재_진행중인_경매의_마지막_입찰자인_사용자.getId()) + ); + 현재_진행중인_경매의_마지막_입찰자인_사용자_리프래시_토큰 = tokenEncoder.encode( + LocalDateTime.now(), + TokenType.REFRESH, + Map.of("userId", 현재_진행중인_경매의_마지막_입찰자인_사용자.getId()) + ); } } diff --git a/backend/ddang/src/test/java/com/ddang/ddang/authentication/application/fixture/AuthenticationUserServiceFixture.java b/backend/ddang/src/test/java/com/ddang/ddang/authentication/application/fixture/AuthenticationUserServiceFixture.java index b7281f200..ea790c774 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/authentication/application/fixture/AuthenticationUserServiceFixture.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/authentication/application/fixture/AuthenticationUserServiceFixture.java @@ -3,7 +3,7 @@ import com.ddang.ddang.image.domain.ProfileImage; import com.ddang.ddang.user.domain.Reliability; import com.ddang.ddang.user.domain.User; -import com.ddang.ddang.user.infrastructure.persistence.JpaUserRepository; +import com.ddang.ddang.user.domain.repository.UserRepository; import org.junit.jupiter.api.BeforeEach; import org.springframework.beans.factory.annotation.Autowired; @@ -11,7 +11,7 @@ public class AuthenticationUserServiceFixture { @Autowired - private JpaUserRepository userRepository; + private UserRepository userRepository; protected User 사용자; protected User 탈퇴한_사용자; diff --git a/backend/ddang/src/test/java/com/ddang/ddang/authentication/application/fixture/BlackListTokenServiceFixture.java b/backend/ddang/src/test/java/com/ddang/ddang/authentication/application/fixture/BlackListTokenServiceFixture.java index 1e9b09bbf..9ee03c94c 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/authentication/application/fixture/BlackListTokenServiceFixture.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/authentication/application/fixture/BlackListTokenServiceFixture.java @@ -3,7 +3,7 @@ import com.ddang.ddang.authentication.domain.BlackListToken; import com.ddang.ddang.authentication.domain.TokenEncoder; import com.ddang.ddang.authentication.domain.TokenType; -import com.ddang.ddang.authentication.infrastructure.persistence.JpaBlackListTokenRepository; +import com.ddang.ddang.authentication.domain.repository.BlackListTokenRepository; import java.time.LocalDateTime; import java.util.Map; import org.junit.jupiter.api.BeforeEach; @@ -13,7 +13,7 @@ public class BlackListTokenServiceFixture { @Autowired - private JpaBlackListTokenRepository blackListTokenRepository; + private BlackListTokenRepository blackListTokenRepository; @Autowired private TokenEncoder tokenEncoder; diff --git a/backend/ddang/src/test/java/com/ddang/ddang/authentication/domain/BlackListTokenTest.java b/backend/ddang/src/test/java/com/ddang/ddang/authentication/domain/BlackListTokenTest.java index 6ce7563b7..7ffb5b945 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/authentication/domain/BlackListTokenTest.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/authentication/domain/BlackListTokenTest.java @@ -16,6 +16,7 @@ class BlackListTokenTest { @Test void 생성자에_유효한_토큰을_전달하면_BlackListToken을_반환한다() { + // when & then assertDoesNotThrow(() -> new BlackListToken(TokenType.ACCESS, "accessToken")); } diff --git a/backend/ddang/src/test/java/com/ddang/ddang/authentication/infrastructure/persistence/BlackListTokenRepositoryImplTest.java b/backend/ddang/src/test/java/com/ddang/ddang/authentication/infrastructure/persistence/BlackListTokenRepositoryImplTest.java new file mode 100644 index 000000000..33c24b852 --- /dev/null +++ b/backend/ddang/src/test/java/com/ddang/ddang/authentication/infrastructure/persistence/BlackListTokenRepositoryImplTest.java @@ -0,0 +1,70 @@ +package com.ddang.ddang.authentication.infrastructure.persistence; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ddang.ddang.authentication.domain.BlackListToken; +import com.ddang.ddang.authentication.domain.TokenType; +import com.ddang.ddang.authentication.domain.repository.BlackListTokenRepository; +import com.ddang.ddang.authentication.infrastructure.persistence.fixture.BlackListTokenRepositoryFixture; +import com.ddang.ddang.configuration.JpaConfiguration; +import com.ddang.ddang.configuration.QuerydslConfiguration; +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.context.annotation.Import; + +@DataJpaTest +@Import({JpaConfiguration.class, QuerydslConfiguration.class}) +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) +@SuppressWarnings("NonAsciiCharacters") +class BlackListTokenRepositoryImplTest extends BlackListTokenRepositoryFixture { + + BlackListTokenRepository blackListTokenRepository; + + @BeforeEach + void setUp(@Autowired final JpaBlackListTokenRepository jpaBlackListTokenRepository) { + blackListTokenRepository = new BlackListTokenRepositoryImpl(jpaBlackListTokenRepository); + } + + @Test + void BlackListToken_엔티티를_저장한다() { + // when + final BlackListToken actual = blackListTokenRepository.save(만료할_토큰1); + + // then + assertThat(actual.getId()).isPositive(); + } + + @Test + void BlackListToken_엔티티_여러개를_저장한다() { + // when + final List actual = blackListTokenRepository.saveAll(List.of(만료할_토큰1, 만료할_토큰2)); + + // then + assertThat(actual).hasSize(2); + } + + @Test + void 블랙리스트로_등록된_토큰인지_확인할때_이미_블랙리스트로_등록된_토큰을_전달하면_참을_반환한다() { + // when + final boolean actual = blackListTokenRepository + .existsByTokenTypeAndToken(TokenType.ACCESS, 만료_토큰_내용); + + // then + assertThat(actual).isTrue(); + } + + @Test + void 블랙리스트로_등록된_토큰인지_확인할때_블랙리스트로_등록되지_않은_토큰을_전달하면_거짓을_반환한다() { + // when + final boolean actual = blackListTokenRepository + .existsByTokenTypeAndToken(TokenType.ACCESS, 만료되지_않은_토큰_내용); + + // then + assertThat(actual).isFalse(); + } +} diff --git a/backend/ddang/src/test/java/com/ddang/ddang/authentication/infrastructure/persistence/JpaBlackListTokenRepositoryTest.java b/backend/ddang/src/test/java/com/ddang/ddang/authentication/infrastructure/persistence/JpaBlackListTokenRepositoryTest.java index 8633ce4a5..6e6e26145 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/authentication/infrastructure/persistence/JpaBlackListTokenRepositoryTest.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/authentication/infrastructure/persistence/JpaBlackListTokenRepositoryTest.java @@ -7,6 +7,7 @@ import com.ddang.ddang.authentication.infrastructure.persistence.fixture.JpaBlackListTokenRepositoryFixture; import com.ddang.ddang.configuration.JpaConfiguration; import com.ddang.ddang.configuration.QuerydslConfiguration; +import java.util.List; import org.junit.jupiter.api.DisplayNameGeneration; import org.junit.jupiter.api.DisplayNameGenerator; import org.junit.jupiter.api.Test; @@ -26,12 +27,21 @@ class JpaBlackListTokenRepositoryTest extends JpaBlackListTokenRepositoryFixture @Test void BlackListToken_엔티티를_저장한다() { // when - final BlackListToken actual = blackListTokenRepository.save(만료할_토큰); + final BlackListToken actual = blackListTokenRepository.save(만료할_토큰1); // then assertThat(actual.getId()).isPositive(); } + @Test + void BlackListToken_엔티티_여러개를_저장한다() { + // when + final List actual = blackListTokenRepository.saveAll(List.of(만료할_토큰1, 만료할_토큰2)); + + // then + assertThat(actual).hasSize(2); + } + @Test void 블랙리스트로_등록된_토큰인지_확인할때_이미_블랙리스트로_등록된_토큰을_전달하면_참을_반환한다() { // when diff --git a/backend/ddang/src/test/java/com/ddang/ddang/authentication/infrastructure/persistence/fixture/BlackListTokenRepositoryFixture.java b/backend/ddang/src/test/java/com/ddang/ddang/authentication/infrastructure/persistence/fixture/BlackListTokenRepositoryFixture.java new file mode 100644 index 000000000..d59bebcd3 --- /dev/null +++ b/backend/ddang/src/test/java/com/ddang/ddang/authentication/infrastructure/persistence/fixture/BlackListTokenRepositoryFixture.java @@ -0,0 +1,30 @@ +package com.ddang.ddang.authentication.infrastructure.persistence.fixture; + +import com.ddang.ddang.authentication.domain.BlackListToken; +import com.ddang.ddang.authentication.domain.TokenType; +import com.ddang.ddang.authentication.domain.repository.BlackListTokenRepository; +import com.ddang.ddang.authentication.infrastructure.persistence.BlackListTokenRepositoryImpl; +import com.ddang.ddang.authentication.infrastructure.persistence.JpaBlackListTokenRepository; +import org.junit.jupiter.api.BeforeEach; +import org.springframework.beans.factory.annotation.Autowired; + +@SuppressWarnings("NonAsciiCharacters") +public class BlackListTokenRepositoryFixture { + + @Autowired + private JpaBlackListTokenRepository jpaBlackListTokenRepository; + + protected String 만료_토큰_내용 = "expired token"; + protected String 만료되지_않은_토큰_내용 = "not expired token"; + protected BlackListToken 만료할_토큰1 = new BlackListToken(TokenType.ACCESS, 만료되지_않은_토큰_내용); + protected BlackListToken 만료할_토큰2 = new BlackListToken(TokenType.ACCESS, 만료되지_않은_토큰_내용); + protected BlackListToken 만료된_토큰 = new BlackListToken(TokenType.ACCESS, 만료_토큰_내용); + + @BeforeEach + void fixtureSetUp() { + final BlackListTokenRepository blackListTokenRepository = + new BlackListTokenRepositoryImpl(jpaBlackListTokenRepository); + + blackListTokenRepository.save(만료된_토큰); + } +} diff --git a/backend/ddang/src/test/java/com/ddang/ddang/authentication/infrastructure/persistence/fixture/JpaBlackListTokenRepositoryFixture.java b/backend/ddang/src/test/java/com/ddang/ddang/authentication/infrastructure/persistence/fixture/JpaBlackListTokenRepositoryFixture.java index 3ea72710a..99a6965df 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/authentication/infrastructure/persistence/fixture/JpaBlackListTokenRepositoryFixture.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/authentication/infrastructure/persistence/fixture/JpaBlackListTokenRepositoryFixture.java @@ -19,7 +19,8 @@ public class JpaBlackListTokenRepositoryFixture { protected String 만료_토큰_내용 = "expired token"; protected String 만료되지_않은_토큰_내용 = "not expired token"; - protected BlackListToken 만료할_토큰 = new BlackListToken(TokenType.ACCESS, 만료되지_않은_토큰_내용); + protected BlackListToken 만료할_토큰1 = new BlackListToken(TokenType.ACCESS, 만료되지_않은_토큰_내용); + protected BlackListToken 만료할_토큰2 = new BlackListToken(TokenType.ACCESS, 만료되지_않은_토큰_내용); protected BlackListToken 만료된_토큰 = new BlackListToken(TokenType.ACCESS, 만료_토큰_내용); @BeforeEach diff --git a/backend/ddang/src/test/java/com/ddang/ddang/authentication/presentation/AuthenticationControllerTest.java b/backend/ddang/src/test/java/com/ddang/ddang/authentication/presentation/AuthenticationControllerTest.java index 13a707c9f..b0646c46d 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/authentication/presentation/AuthenticationControllerTest.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/authentication/presentation/AuthenticationControllerTest.java @@ -1,6 +1,5 @@ package com.ddang.ddang.authentication.presentation; -import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.BDDMockito.given; @@ -20,6 +19,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import com.ddang.ddang.authentication.application.exception.InvalidWithdrawalException; +import com.ddang.ddang.authentication.application.exception.WithdrawalNotAllowedException; import com.ddang.ddang.authentication.configuration.Oauth2TypeConverter; import com.ddang.ddang.authentication.domain.exception.InvalidTokenException; import com.ddang.ddang.authentication.domain.exception.UnsupportedSocialLoginException; @@ -59,7 +59,7 @@ void setUp() { @Test void 소셜_로그인을_지원하는_타입과_소셜_로그인_토큰을_전달하면_accessToken과_refreshToken을_반환한다() throws Exception { // given - given(authenticationService.login(eq(지원하는_소셜_로그인_타입), anyString(), anyString())).willReturn(발급된_토큰); + given(authenticationService.login(eq(지원하는_소셜_로그인_타입), anyString(), anyString())).willReturn(로그인한_사용자_정보); // when & then final ResultActions resultActions = @@ -70,7 +70,8 @@ void setUp() { .andExpectAll( status().isOk(), jsonPath("$.accessToken").exists(), - jsonPath("$.refreshToken").exists() + jsonPath("$.refreshToken").exists(), + jsonPath("$.isSignUpUser").exists() ); login_문서화(resultActions); @@ -201,11 +202,11 @@ void setUp() { @Test void ouath2Type과_accessToken과_refreshToken을_전달하면_탈퇴한다() throws Exception { // given - willDoNothing().given(authenticationService).withdrawal(any(), anyString(), anyString()); + willDoNothing().given(authenticationService).withdrawal(anyString(), anyString()); // when & then final ResultActions resultActions = - mockMvc.perform(RestDocumentationRequestBuilders.post("/oauth2/withdrawal/{oauth2Type}", 소셜_로그인_타입) + mockMvc.perform(RestDocumentationRequestBuilders.post("/oauth2/withdrawal") .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(유효한_회원탈퇴_요청)) .header(HttpHeaders.AUTHORIZATION, 유효한_액세스_토큰_내용) @@ -221,17 +222,55 @@ void setUp() { void ouath2Type과_accessToken과_refreshToken을_전달시_이미_탈퇴_혹은_존재하지_않아_권한이_없는_회원인_경우_403을_반환한다() throws Exception { // given willThrow(new InvalidWithdrawalException("탈퇴에 대한 권한 없습니다.")).given(authenticationService) - .withdrawal(any(), anyString(), anyString()); + .withdrawal(anyString(), anyString()); // when & then - mockMvc.perform(RestDocumentationRequestBuilders.post("/oauth2/withdrawal/{oauth2Type}", 소셜_로그인_타입) + mockMvc.perform(RestDocumentationRequestBuilders.post("/oauth2/withdrawal") .header(HttpHeaders.AUTHORIZATION, 유효한_액세스_토큰_내용) .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(유효하지_않은_회원탈퇴_요청)) ) .andExpectAll( status().isForbidden(), - jsonPath("$.message").value("탈퇴에 대한 권한 없습니다.") + jsonPath("$.message").exists() + ); + } + + @Test + void ouath2Type과_accessToken과_refreshToken을_전달시_진행중인_경매를_등록한_회원인_경우_400을_반환한다() throws Exception { + // given + willThrow(new WithdrawalNotAllowedException("등록한 경매 중 현재 진행 중인 것이 있기에 탈퇴할 수 없습니다.")) + .given(authenticationService) + .withdrawal(anyString(), anyString()); + + // when & then + mockMvc.perform(RestDocumentationRequestBuilders.post("/oauth2/withdrawal") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(유효한_회원탈퇴_요청)) + .header(HttpHeaders.AUTHORIZATION, 유효한_액세스_토큰_내용) + ) + .andExpectAll( + status().isBadRequest(), + jsonPath("$.message").exists() + ); + } + + @Test + void ouath2Type과_accessToken과_refreshToken을_전달시_진행중인_경매의_마지막_입찰자_회원인_경우_400을_반환한다() throws Exception { + // given + willThrow(new WithdrawalNotAllowedException("마지막 입찰자로 등록되어 있는 것이 있기에 탈퇴할 수 없습니다.")) + .given(authenticationService) + .withdrawal(anyString(), anyString()); + + // when & then + mockMvc.perform(RestDocumentationRequestBuilders.post("/oauth2/withdrawal") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(유효한_회원탈퇴_요청)) + .header(HttpHeaders.AUTHORIZATION, 유효한_액세스_토큰_내용) + ) + .andExpectAll( + status().isBadRequest(), + jsonPath("$.message").exists() ); } @@ -249,7 +288,9 @@ void setUp() { fieldWithPath("accessToken").type(JsonFieldType.STRING) .description("Access Token"), fieldWithPath("refreshToken").type(JsonFieldType.STRING) - .description("Refresh Token") + .description("Refresh Token"), + fieldWithPath("isSignUpUser").type(JsonFieldType.BOOLEAN) + .description("최초 로그인 여부(회원가입)") ) ) ); @@ -301,9 +342,6 @@ void setUp() { private void withdrawal_문서화(final ResultActions resultActions) throws Exception { resultActions.andDo( restDocs.document( - pathParameters( - parameterWithName("oauth2Type").description("소셜 로그인을 할 서비스 선택(kakao로 고정)") - ), requestHeaders( headerWithName("Authorization").description("회원 Bearer 인증 정보") ), diff --git a/backend/ddang/src/test/java/com/ddang/ddang/authentication/presentation/fixture/AuthenticationControllerFixture.java b/backend/ddang/src/test/java/com/ddang/ddang/authentication/presentation/fixture/AuthenticationControllerFixture.java index ee3145fee..a09ec833e 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/authentication/presentation/fixture/AuthenticationControllerFixture.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/authentication/presentation/fixture/AuthenticationControllerFixture.java @@ -1,5 +1,6 @@ package com.ddang.ddang.authentication.presentation.fixture; +import com.ddang.ddang.authentication.application.dto.LoginInformationDto; import com.ddang.ddang.authentication.application.dto.TokenDto; import com.ddang.ddang.authentication.infrastructure.oauth2.Oauth2Type; import com.ddang.ddang.authentication.presentation.dto.request.LoginTokenRequest; @@ -17,6 +18,7 @@ public class AuthenticationControllerFixture extends CommonControllerSliceTest { protected String 유효한_액세스_토큰_내용 = "Bearer accessToken"; protected String 만료된_액세스_토큰_내용 = "Bearer accessToken"; protected TokenDto 발급된_토큰 = new TokenDto(유효한_액세스_토큰_내용, "Bearer refreshToken"); + protected LoginInformationDto 로그인한_사용자_정보 = new LoginInformationDto(발급된_토큰, false); protected LoginTokenRequest 유효한_로그인_요청 = new LoginTokenRequest("kakaoAccessToken", "deviceToken"); protected LoginTokenRequest 유효하지_않은_로그인_요청 = new LoginTokenRequest("kakaoAccessToken", "deviceToken"); protected RefreshTokenRequest 유효한_토큰_재발급_요청 = new RefreshTokenRequest("Bearer refreshToken"); diff --git a/backend/ddang/src/test/java/com/ddang/ddang/bid/application/fixture/BidServiceFixture.java b/backend/ddang/src/test/java/com/ddang/ddang/bid/application/fixture/BidServiceFixture.java index 022acf6e8..16b5cf6f2 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/bid/application/fixture/BidServiceFixture.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/bid/application/fixture/BidServiceFixture.java @@ -3,17 +3,17 @@ import com.ddang.ddang.auction.domain.Auction; import com.ddang.ddang.auction.domain.BidUnit; import com.ddang.ddang.auction.domain.Price; -import com.ddang.ddang.auction.infrastructure.persistence.JpaAuctionRepository; +import com.ddang.ddang.auction.domain.repository.AuctionRepository; import com.ddang.ddang.bid.application.dto.CreateBidDto; import com.ddang.ddang.bid.domain.Bid; import com.ddang.ddang.bid.domain.BidPrice; -import com.ddang.ddang.bid.infrastructure.persistence.JpaBidRepository; +import com.ddang.ddang.bid.domain.repository.BidRepository; import com.ddang.ddang.image.domain.AuctionImage; import com.ddang.ddang.image.domain.ProfileImage; import com.ddang.ddang.notification.domain.NotificationStatus; import com.ddang.ddang.user.domain.Reliability; import com.ddang.ddang.user.domain.User; -import com.ddang.ddang.user.infrastructure.persistence.JpaUserRepository; +import com.ddang.ddang.user.domain.repository.UserRepository; import org.junit.jupiter.api.BeforeEach; import org.springframework.beans.factory.annotation.Autowired; @@ -24,13 +24,13 @@ public class BidServiceFixture { @Autowired - private JpaUserRepository userRepository; + private UserRepository userRepository; @Autowired - private JpaAuctionRepository auctionRepository; + private AuctionRepository auctionRepository; @Autowired - private JpaBidRepository bidRepository; + private BidRepository bidRepository; protected NotificationStatus 알림_성공 = NotificationStatus.SUCCESS; protected String 이미지_절대_url = "https://3-ddang.store/auctions/images"; @@ -126,11 +126,20 @@ void setUp() { .build(); 삭제된_경매.delete(); - userRepository.saveAll(List.of(판매자, 입찰자1, 입찰자2)); + userRepository.save(판매자); + userRepository.save(입찰자1); + userRepository.save(입찰자2); + 경매1.addAuctionImages(List.of(경매_이미지1)); 경매2.addAuctionImages(List.of(경매_이미지2)); 경매3.addAuctionImages(List.of(경매_이미지3)); - auctionRepository.saveAll(List.of(경매1, 경매2, 경매3, 입찰_내역이_하나_있던_경매, 종료된_경매, 삭제된_경매)); + + auctionRepository.save(경매1); + auctionRepository.save(경매2); + auctionRepository.save(경매3); + auctionRepository.save(입찰_내역이_하나_있던_경매); + auctionRepository.save(종료된_경매); + auctionRepository.save(삭제된_경매); final Bid bid1 = new Bid(경매1, 입찰자1, new BidPrice(1_000)); final Bid bid2 = new Bid(경매2, 입찰자1, new BidPrice(1_000)); diff --git a/backend/ddang/src/test/java/com/ddang/ddang/bid/infrastructure/persistence/BidRepositoryImplTest.java b/backend/ddang/src/test/java/com/ddang/ddang/bid/infrastructure/persistence/BidRepositoryImplTest.java new file mode 100644 index 000000000..d49dc0faa --- /dev/null +++ b/backend/ddang/src/test/java/com/ddang/ddang/bid/infrastructure/persistence/BidRepositoryImplTest.java @@ -0,0 +1,68 @@ +package com.ddang.ddang.bid.infrastructure.persistence; + +import com.ddang.ddang.bid.domain.Bid; +import com.ddang.ddang.bid.domain.repository.BidRepository; +import com.ddang.ddang.bid.infrastructure.persistence.fixture.BidRepositoryImplFixture; +import com.ddang.ddang.configuration.JpaConfiguration; +import com.ddang.ddang.configuration.QuerydslConfiguration; +import org.assertj.core.api.SoftAssertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.context.annotation.Import; + +import java.util.List; +import java.util.Optional; + +import static org.assertj.core.api.Assertions.assertThat; + +@DataJpaTest +@Import({JpaConfiguration.class, QuerydslConfiguration.class}) +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) +@SuppressWarnings("NonAsciiCharacters") +class BidRepositoryImplTest extends BidRepositoryImplFixture { + + BidRepository bidRepository; + + @BeforeEach + void setUp(@Autowired final JpaBidRepository jpaBidRepository) { + bidRepository = new BidRepositoryImpl(jpaBidRepository); + } + + @Test + void 입찰을_저장한다() { + // given + final Bid bid = new Bid(경매1, 입찰자1, 입찰액); + + // when + final Bid actual = bidRepository.save(bid); + + // then + assertThat(actual.getId()).isPositive(); + } + + @Test + void 특정_경매의_입찰을_모두_조회한다() { + // when + final List actual = bidRepository.findAllByAuctionId(경매1.getId()); + + // then + SoftAssertions.assertSoftly(softAssertions -> { + softAssertions.assertThat(actual).hasSize(2); + softAssertions.assertThat(actual.get(0).getId()).isEqualTo(경매1의_입찰1.getId()); + softAssertions.assertThat(actual.get(1).getId()).isEqualTo(경매1의_입찰2겸_마지막_입찰.getId()); + }); + } + + @Test + void 특정_경매의_마지막_입찰을_조회한다() { + // when + final Optional actual = bidRepository.findLastBidByAuctionId(경매1.getId()); + + // then + assertThat(actual.get().getId()).isEqualTo(경매1의_입찰2겸_마지막_입찰.getId()); + } +} diff --git a/backend/ddang/src/test/java/com/ddang/ddang/bid/infrastructure/persistence/JpaBidRepositoryTest.java b/backend/ddang/src/test/java/com/ddang/ddang/bid/infrastructure/persistence/JpaBidRepositoryTest.java index ab298d42b..5aafd4ece 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/bid/infrastructure/persistence/JpaBidRepositoryTest.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/bid/infrastructure/persistence/JpaBidRepositoryTest.java @@ -15,6 +15,7 @@ import org.springframework.context.annotation.Import; import java.util.List; +import java.util.Optional; import static org.assertj.core.api.Assertions.assertThat; @@ -24,9 +25,6 @@ @SuppressWarnings("NonAsciiCharacters") class JpaBidRepositoryTest extends JpaBidRepositoryFixture { - @PersistenceContext - EntityManager em; - @Autowired JpaBidRepository bidRepository; @@ -39,25 +37,13 @@ class JpaBidRepositoryTest extends JpaBidRepositoryFixture { final Bid actual = bidRepository.save(bid); // then - em.flush(); - em.clear(); - assertThat(actual.getId()).isPositive(); } - @Test - void 특정_경매에_입찰이_존재하는지_확인한다() { - // when - final boolean actual = bidRepository.existsById(경매1의_입찰1.getId()); - - // then - assertThat(actual).isTrue(); - } - @Test void 특정_경매의_입찰을_모두_조회한다() { // when - final List actual = bidRepository.findByAuctionIdOrderByIdAsc(경매1.getId()); + final List actual = bidRepository.findAllByAuctionIdOrderByIdAsc(경매1.getId()); // then SoftAssertions.assertSoftly(softAssertions -> { @@ -70,9 +56,9 @@ class JpaBidRepositoryTest extends JpaBidRepositoryFixture { @Test void 특정_경매의_마지막_입찰을_조회한다() { // when - final Bid actual = bidRepository.findLastBidByAuctionId(경매1.getId()); + final Optional actual = bidRepository.findLastBidByAuctionId(경매1.getId()); // then - assertThat(actual.getId()).isEqualTo(경매1의_입찰2겸_마지막_입찰.getId()); + assertThat(actual.get().getId()).isEqualTo(경매1의_입찰2겸_마지막_입찰.getId()); } } diff --git a/backend/ddang/src/test/java/com/ddang/ddang/bid/infrastructure/persistence/fixture/BidRepositoryImplFixture.java b/backend/ddang/src/test/java/com/ddang/ddang/bid/infrastructure/persistence/fixture/BidRepositoryImplFixture.java new file mode 100644 index 000000000..fbab9a1b0 --- /dev/null +++ b/backend/ddang/src/test/java/com/ddang/ddang/bid/infrastructure/persistence/fixture/BidRepositoryImplFixture.java @@ -0,0 +1,126 @@ +package com.ddang.ddang.bid.infrastructure.persistence.fixture; + +import com.ddang.ddang.auction.domain.Auction; +import com.ddang.ddang.auction.domain.BidUnit; +import com.ddang.ddang.auction.domain.Price; +import com.ddang.ddang.auction.domain.repository.AuctionRepository; +import com.ddang.ddang.auction.infrastructure.persistence.AuctionRepositoryImpl; +import com.ddang.ddang.auction.infrastructure.persistence.JpaAuctionRepository; +import com.ddang.ddang.auction.infrastructure.persistence.QuerydslAuctionRepository; +import com.ddang.ddang.bid.domain.Bid; +import com.ddang.ddang.bid.domain.BidPrice; +import com.ddang.ddang.bid.domain.repository.BidRepository; +import com.ddang.ddang.bid.infrastructure.persistence.BidRepositoryImpl; +import com.ddang.ddang.bid.infrastructure.persistence.JpaBidRepository; +import com.ddang.ddang.category.domain.Category; +import com.ddang.ddang.category.infrastructure.persistence.JpaCategoryRepository; +import com.ddang.ddang.image.domain.AuctionImage; +import com.ddang.ddang.image.domain.ProfileImage; +import com.ddang.ddang.user.domain.Reliability; +import com.ddang.ddang.user.domain.User; +import com.ddang.ddang.user.domain.repository.UserRepository; +import com.ddang.ddang.user.infrastructure.persistence.JpaUserRepository; +import com.ddang.ddang.user.infrastructure.persistence.UserRepositoryImpl; +import com.querydsl.jpa.impl.JPAQueryFactory; +import org.junit.jupiter.api.BeforeEach; +import org.springframework.beans.factory.annotation.Autowired; + +import java.time.LocalDateTime; +import java.util.List; + +@SuppressWarnings("NonAsciiCharacters") +public class BidRepositoryImplFixture { + + @Autowired + private JpaCategoryRepository categoryRepository; + + private UserRepository userRepository; + + private AuctionRepository auctionRepository; + + private BidRepository bidRepository; + + protected User 판매자; + protected Auction 경매1; + protected Auction 경매2; + protected User 입찰자1; + protected BidPrice 입찰액; + protected Bid 경매1의_입찰1; + protected Bid 경매1의_입찰2겸_마지막_입찰; + + @BeforeEach + void fixtureSetUp( + @Autowired final JPAQueryFactory jpaQueryFactory, + @Autowired final JpaUserRepository jpaUserRepository, + @Autowired final JpaAuctionRepository jpaAuctionRepository, + @Autowired final JpaBidRepository jpaBidRepository + ) { + userRepository = new UserRepositoryImpl(jpaUserRepository); + auctionRepository = new AuctionRepositoryImpl(jpaAuctionRepository, new QuerydslAuctionRepository(jpaQueryFactory)); + bidRepository = new BidRepositoryImpl(jpaBidRepository); + + final ProfileImage 프로필_이미지 = new ProfileImage("프로필.jpg", "프로필.jpg"); + 판매자 = User.builder() + .name("판매자") + .profileImage(프로필_이미지) + .reliability(new Reliability(4.7d)) + .oauthId("12345") + .build(); + + final Category 전자기기_카테고리 = new Category("전자기기"); + final Category 전자기기_서브_노트북_카테고리 = new Category("노트북 카테고리"); + 전자기기_카테고리.addSubCategory(전자기기_서브_노트북_카테고리); + final AuctionImage 경매_이미지 = new AuctionImage("경매이미지.jpg", "경매이미지.jpg"); + 경매1 = Auction.builder() + .seller(판매자) + .title("경매 상품") + .description("이것은 경매 상품입니다.") + .subCategory(전자기기_서브_노트북_카테고리) + .bidUnit(new BidUnit(1_000)) + .startPrice(new Price(1_000)) + .closingTime(LocalDateTime.now()) + .build(); + 경매1.addAuctionImages(List.of(경매_이미지)); + 경매2 = Auction.builder() + .seller(판매자) + .title("경매 상품") + .description("이것은 경매 상품입니다.") + .subCategory(전자기기_서브_노트북_카테고리) + .bidUnit(new BidUnit(1_000)) + .startPrice(new Price(1_000)) + .closingTime(LocalDateTime.now()) + .build(); + 경매2.addAuctionImages(List.of(경매_이미지)); + + 입찰자1 = User.builder() + .name("입찰자1") + .profileImage(프로필_이미지) + .reliability(new Reliability(4.7d)) + .oauthId("12346") + .build(); + final User 입찰자2 = User.builder() + .name("입찰자2") + .profileImage(프로필_이미지) + .reliability(new Reliability(4.7d)) + .oauthId("12346") + .build(); + + 입찰액 = new BidPrice(10_000); + + 경매1의_입찰1 = new Bid(경매1, 입찰자1, 입찰액); + 경매1의_입찰2겸_마지막_입찰 = new Bid(경매1, 입찰자2, 입찰액); + final Bid 경매2의_입찰1 = new Bid(경매2, 입찰자1, 입찰액); + + userRepository.save(판매자); + userRepository.save(입찰자1); + userRepository.save(입찰자2); + + categoryRepository.saveAll(List.of(전자기기_카테고리, 전자기기_서브_노트북_카테고리)); + auctionRepository.save(경매1); + auctionRepository.save(경매2); + + bidRepository.save(경매1의_입찰1); + bidRepository.save(경매1의_입찰2겸_마지막_입찰); + bidRepository.save(경매2의_입찰1); + } +} diff --git a/backend/ddang/src/test/java/com/ddang/ddang/bid/presentation/BidControllerTest.java b/backend/ddang/src/test/java/com/ddang/ddang/bid/presentation/BidControllerTest.java index 13b617c15..6ec3319da 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/bid/presentation/BidControllerTest.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/bid/presentation/BidControllerTest.java @@ -99,7 +99,6 @@ void setUp() { create_문서화(resultActions); } - // TODO: 2023-08-06 예외 케이스 api 문서화의 경우 예외에 대한 변경이 없을 때 추가할 것 @Test void 해당_경매가_없는_경우_입찰시_404를_반환한다() throws Exception { // given @@ -378,14 +377,14 @@ private static Stream provideBidRequestWithNotPositiveBidPrice ) .andExpectAll( status().isOk(), - jsonPath("$.bids.[0].name", is(입찰_정보_dto1.name())), - jsonPath("$.bids.[0].profileImage").exists(), - jsonPath("$.bids.[0].price", is(입찰_정보_dto1.price())), - jsonPath("$.bids.[0].bidTime").exists(), - jsonPath("$.bids.[1].name", is(입찰_정보_dto2.name())), - jsonPath("$.bids.[1].profileImage").exists(), - jsonPath("$.bids.[1].price", is(입찰_정보_dto2.price())), - jsonPath("$.bids.[1].bidTime").exists() + jsonPath("$.[0].name", is(입찰_정보_dto1.name())), + jsonPath("$.[0].profileImage").exists(), + jsonPath("$.[0].price", is(입찰_정보_dto1.price())), + jsonPath("$.[0].bidTime").exists(), + jsonPath("$.[1].name", is(입찰_정보_dto2.name())), + jsonPath("$.[1].profileImage").exists(), + jsonPath("$.[1].price", is(입찰_정보_dto2.price())), + jsonPath("$.[1].bidTime").exists() ); readAllByAuctionId_문서화(resultActions); @@ -424,16 +423,16 @@ private static Stream provideBidRequestWithNotPositiveBidPrice resultActions.andDo( restDocs.document( responseFields( - fieldWithPath("bids.[]").type(JsonFieldType.ARRAY) - .description("특정 경매의 모든 입찰 목록"), - fieldWithPath("bids.[].name").type(JsonFieldType.STRING) - .description("입찰한 사용자의 닉네임"), - fieldWithPath("bids.[].profileImage").type(JsonFieldType.STRING) - .description("입찰한 사용자의 프로필 이미지 URL"), - fieldWithPath("bids.[].price").type(JsonFieldType.NUMBER) - .description("입찰한 금액"), - fieldWithPath("bids.[].bidTime").type(JsonFieldType.STRING) - .description("입찰한 시간") + fieldWithPath("[]").type(JsonFieldType.ARRAY) + .description("특정 경매의 모든 입찰 목록"), + fieldWithPath("[].name").type(JsonFieldType.STRING) + .description("입찰한 사용자의 닉네임"), + fieldWithPath("[].profileImage").type(JsonFieldType.STRING) + .description("입찰한 사용자의 프로필 이미지 URL"), + fieldWithPath("[].price").type(JsonFieldType.NUMBER) + .description("입찰한 금액"), + fieldWithPath("[].bidTime").type(JsonFieldType.STRING) + .description("입찰한 시간") ) ) ); diff --git a/backend/ddang/src/test/java/com/ddang/ddang/category/infrastructure/persistence/CategoryRepositoryImplTest.java b/backend/ddang/src/test/java/com/ddang/ddang/category/infrastructure/persistence/CategoryRepositoryImplTest.java new file mode 100644 index 000000000..11a2b389a --- /dev/null +++ b/backend/ddang/src/test/java/com/ddang/ddang/category/infrastructure/persistence/CategoryRepositoryImplTest.java @@ -0,0 +1,76 @@ +package com.ddang.ddang.category.infrastructure.persistence; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.ddang.ddang.category.domain.Category; +import com.ddang.ddang.category.domain.repository.CategoryRepository; +import com.ddang.ddang.category.infrastructure.persistence.fixture.CategoryRepositoryFixture; +import com.ddang.ddang.configuration.QuerydslConfiguration; +import java.util.List; +import java.util.Optional; +import org.assertj.core.api.SoftAssertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.context.annotation.Import; + +@DataJpaTest +@Import(QuerydslConfiguration.class) +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) +@SuppressWarnings("NonAsciiCharacters") +class CategoryRepositoryImplTest extends CategoryRepositoryFixture { + + CategoryRepository categoryRepository; + + @BeforeEach + void setUp(@Autowired final JpaCategoryRepository jpaCategoryRepository) { + categoryRepository = new CategoryRepositoryImpl(jpaCategoryRepository); + } + + @Test + void 모든_메인_카테고리를_조회한다() { + // when + final List actual = categoryRepository.findMainAllByMainCategoryIsNull(); + + // then + SoftAssertions.assertSoftly(softAssertions -> { + softAssertions.assertThat(actual).hasSize(2); + softAssertions.assertThat(actual.get(0)).isEqualTo(가구_카테고리); + softAssertions.assertThat(actual.get(1)).isEqualTo(전자기기_카테고리); + }); + } + + @Test + void 메인_카테고리에_해당하는_모든_서브_카테고리를_조회한다() { + // when + final List actual = categoryRepository.findSubAllByMainCategoryId(가구_카테고리.getId()); + + // then + SoftAssertions.assertSoftly(softAssertions -> { + softAssertions.assertThat(actual).hasSize(2); + softAssertions.assertThat(actual.get(0)).isEqualTo(가구_서브_의자_카테고리); + softAssertions.assertThat(actual.get(1)).isEqualTo(가구_서브_책상_카테고리); + }); + } + + @Test + void 서브_카테고리를_조회한다() { + // when + final Optional actual = categoryRepository.findSubCategoryById(가구_서브_의자_카테고리.getId()); + + // then + assertThat(actual).contains(가구_서브_의자_카테고리); + } + + @Test + void 서브_카테고리가_아닌_카테고리의_아이디를_전달하면_빈_Optional을_반환한다() { + // when + final Optional actual = categoryRepository.findSubCategoryById(가구_카테고리.getId()); + + // then + assertThat(actual).isEmpty(); + } +} diff --git a/backend/ddang/src/test/java/com/ddang/ddang/category/infrastructure/persistence/fixture/CategoryRepositoryFixture.java b/backend/ddang/src/test/java/com/ddang/ddang/category/infrastructure/persistence/fixture/CategoryRepositoryFixture.java new file mode 100644 index 000000000..eb1ef4f78 --- /dev/null +++ b/backend/ddang/src/test/java/com/ddang/ddang/category/infrastructure/persistence/fixture/CategoryRepositoryFixture.java @@ -0,0 +1,40 @@ +package com.ddang.ddang.category.infrastructure.persistence.fixture; + +import com.ddang.ddang.category.domain.Category; +import com.ddang.ddang.category.infrastructure.persistence.JpaCategoryRepository; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import org.junit.jupiter.api.BeforeEach; +import org.springframework.beans.factory.annotation.Autowired; + +@SuppressWarnings("NonAsciiCharacters") +public class CategoryRepositoryFixture { + + @PersistenceContext + private EntityManager em; + + @Autowired + private JpaCategoryRepository categoryRepository; + + protected Category 가구_카테고리; + protected Category 전자기기_카테고리; + protected Category 가구_서브_의자_카테고리; + protected Category 가구_서브_책상_카테고리; + + @BeforeEach + void setUp() { + 가구_카테고리 = new Category("가구"); + 전자기기_카테고리 = new Category("전자기기"); + 가구_서브_의자_카테고리 = new Category("의자"); + 가구_서브_책상_카테고리 = new Category("책상"); + + 가구_카테고리.addSubCategory(가구_서브_의자_카테고리); + 가구_카테고리.addSubCategory(가구_서브_책상_카테고리); + + categoryRepository.save(가구_카테고리); + categoryRepository.save(전자기기_카테고리); + + em.flush(); + em.clear(); + } +} diff --git a/backend/ddang/src/test/java/com/ddang/ddang/chat/application/ChatRoomServiceTest.java b/backend/ddang/src/test/java/com/ddang/ddang/chat/application/ChatRoomServiceTest.java index 4ce53a7a7..de0cccb21 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/chat/application/ChatRoomServiceTest.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/chat/application/ChatRoomServiceTest.java @@ -164,11 +164,12 @@ class ChatRoomServiceTest extends ChatRoomServiceFixture { } @Test - void 지정한_경매_아이디와_관련된_채팅방을_조회할_때_조회를_요청한_사용자_정보를_찾을_수_없다면_예외가_발생한다() { - // when & then - assertThatThrownBy(() -> chatRoomService.readChatInfoByAuctionId(판매자_엔초_구매자_지토_경매.getId(), 존재하지_않는_사용자_정보)) - .isInstanceOf(UserNotFoundException.class) - .hasMessage("회원 정보를 찾을 수 없습니다."); + void 지정한_경매_아이디와_관련된_채팅방을_조회할_때_조회를_요청한_사용자_정보를_찾을_수_없다면_무조건_채팅방_아이디_null과_참여가능여부_거짓을_반환한다() { + // when + final ReadChatRoomDto actual = chatRoomService.readChatInfoByAuctionId(판매자_엔초_구매자_지토_경매.getId(), 존재하지_않는_사용자_정보); + + // then + assertThat(actual).isEqualTo(채팅방_없고_참여_불가능); } @Test @@ -180,7 +181,7 @@ class ChatRoomServiceTest extends ChatRoomServiceFixture { } @Test - void 지정한_경매_아이디와_관련된_채팅방을_조회할_때_채팅방을_찾을_수_없다면_채팅방_아이디_null과_참여가능여부를_반환한다() { + void 지정한_경매_아이디와_관련된_채팅방을_조회할_때_채팅방을_찾을_수_없다면_채팅방_아이디_null과_참여가능을_반환한다() { // when final ReadChatRoomDto actual = chatRoomService.readChatInfoByAuctionId(채팅방이_없는_경매.getId(), 판매자_회원_정보); diff --git a/backend/ddang/src/test/java/com/ddang/ddang/chat/application/fixture/ChatRoomServiceFixture.java b/backend/ddang/src/test/java/com/ddang/ddang/chat/application/fixture/ChatRoomServiceFixture.java index 9d3adcd1d..234913411 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/chat/application/fixture/ChatRoomServiceFixture.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/chat/application/fixture/ChatRoomServiceFixture.java @@ -4,11 +4,11 @@ import com.ddang.ddang.auction.domain.Auction; import com.ddang.ddang.auction.domain.BidUnit; import com.ddang.ddang.auction.domain.Price; -import com.ddang.ddang.auction.infrastructure.persistence.JpaAuctionRepository; +import com.ddang.ddang.auction.domain.repository.AuctionRepository; import com.ddang.ddang.authentication.domain.dto.AuthenticationUserInfo; import com.ddang.ddang.bid.domain.Bid; import com.ddang.ddang.bid.domain.BidPrice; -import com.ddang.ddang.bid.infrastructure.persistence.JpaBidRepository; +import com.ddang.ddang.bid.domain.repository.BidRepository; import com.ddang.ddang.category.domain.Category; import com.ddang.ddang.category.infrastructure.persistence.JpaCategoryRepository; import com.ddang.ddang.chat.application.dto.CreateChatRoomDto; @@ -19,14 +19,14 @@ import com.ddang.ddang.chat.application.dto.ReadUserInChatRoomDto; import com.ddang.ddang.chat.domain.ChatRoom; import com.ddang.ddang.chat.domain.Message; -import com.ddang.ddang.chat.infrastructure.persistence.JpaChatRoomRepository; -import com.ddang.ddang.chat.infrastructure.persistence.JpaMessageRepository; -import com.ddang.ddang.chat.infrastructure.persistence.dto.ChatRoomAndImageDto; +import com.ddang.ddang.chat.domain.dto.ChatRoomAndImageDto; +import com.ddang.ddang.chat.domain.repository.ChatRoomRepository; +import com.ddang.ddang.chat.domain.repository.MessageRepository; import com.ddang.ddang.image.domain.AuctionImage; import com.ddang.ddang.image.domain.ProfileImage; import com.ddang.ddang.user.domain.Reliability; import com.ddang.ddang.user.domain.User; -import com.ddang.ddang.user.infrastructure.persistence.JpaUserRepository; +import com.ddang.ddang.user.domain.repository.UserRepository; import org.junit.jupiter.api.BeforeEach; import org.springframework.beans.factory.annotation.Autowired; @@ -37,22 +37,22 @@ public class ChatRoomServiceFixture { @Autowired - private JpaCategoryRepository categoryRepository; + private AuctionRepository auctionRepository; @Autowired - private JpaUserRepository userRepository; + private JpaCategoryRepository categoryRepository; @Autowired - private JpaAuctionRepository auctionRepository; + private UserRepository userRepository; @Autowired - private JpaBidRepository bidRepository; + private BidRepository bidRepository; @Autowired - private JpaChatRoomRepository chatRoomRepository; + private ChatRoomRepository chatRoomRepository; @Autowired - private JpaMessageRepository messageRepository; + private MessageRepository messageRepository; protected User 판매자; protected User 구매자; @@ -82,6 +82,7 @@ public class ChatRoomServiceFixture { protected ReadChatRoomDto 엔초_지토_채팅방_정보_및_참여_가능; protected ReadChatRoomDto 엔초_지토_채팅방_정보_및_참여_불가능; protected ReadChatRoomDto 채팅방은_아직_없지만_참여_가능; + protected ReadChatRoomDto 채팅방_없고_참여_불가능; protected ReadChatRoomWithLastMessageDto 엔초_채팅_목록의_제이미_엔초_채팅방_정보; protected ReadChatRoomWithLastMessageDto 엔초_채팅_목록의_엔초_지토_채팅방_정보; @@ -104,120 +105,133 @@ void setUp() { new AuctionImage("제이미의_대표 이미지가_아닌_경매_이미지.png", "제이미의_대표 이미지가_아닌_경매_이미지.png"); 판매자 = User.builder() - .name("판매자") - .profileImage(프로필_이미지) - .reliability(new Reliability(4.7d)) - .oauthId("12345") - .build(); + .name("판매자") + .profileImage(프로필_이미지) + .reliability(new Reliability(4.7d)) + .oauthId("12345") + .build(); 구매자 = User.builder() - .name("구매자") - .profileImage(프로필_이미지) - .reliability(new Reliability(4.7d)) - .oauthId("12346") - .build(); - 엔초 = User.builder() - .name("엔초") + .name("구매자") .profileImage(프로필_이미지) .reliability(new Reliability(4.7d)) .oauthId("12346") .build(); + 엔초 = User.builder() + .name("엔초") + .profileImage(프로필_이미지) + .reliability(new Reliability(4.7d)) + .oauthId("12346") + .build(); 제이미 = User.builder() - .name("제이미") - .profileImage(프로필_이미지) - .reliability(new Reliability(4.7d)) - .oauthId("12347") - .build(); - 지토 = User.builder() - .name("지토") + .name("제이미") .profileImage(프로필_이미지) .reliability(new Reliability(4.7d)) - .oauthId("12348") + .oauthId("12347") .build(); + 지토 = User.builder() + .name("지토") + .profileImage(프로필_이미지) + .reliability(new Reliability(4.7d)) + .oauthId("12348") + .build(); 경매에_참여한_적_없는_사용자 = User.builder() - .name("외부인") - .profileImage(프로필_이미지) - .reliability(new Reliability(4.7d)) - .oauthId("12349") - .build(); - userRepository.saveAll(List.of(판매자, 구매자, 엔초, 제이미, 지토, 경매에_참여한_적_없는_사용자)); + .name("외부인") + .profileImage(프로필_이미지) + .reliability(new Reliability(4.7d)) + .oauthId("12349") + .build(); + userRepository.save(판매자); + userRepository.save(구매자); + userRepository.save(엔초); + userRepository.save(제이미); + userRepository.save(지토); + userRepository.save(경매에_참여한_적_없는_사용자); 채팅방이_없는_경매 = Auction.builder() - .seller(판매자) - .title("맥북") - .description("맥북 팔아요") - .subCategory(전자기기_서브_노트북_카테고리) - .startPrice(new Price(10_000)) - .bidUnit(new BidUnit(1_000)) - .closingTime(LocalDateTime.now()) - .build(); + .seller(판매자) + .title("맥북") + .description("맥북 팔아요") + .subCategory(전자기기_서브_노트북_카테고리) + .startPrice(new Price(10_000)) + .bidUnit(new BidUnit(1_000)) + .closingTime(LocalDateTime.now()) + .build(); final Auction 종료되지_않은_경매 = Auction.builder() - .seller(판매자) - .title("맥북") - .description("맥북 팔아요") - .subCategory(전자기기_서브_노트북_카테고리) - .startPrice(new Price(10_000)) - .bidUnit(new BidUnit(1_000)) - .closingTime(LocalDateTime.now().plusDays(10L)) - .build(); + .seller(판매자) + .title("맥북") + .description("맥북 팔아요") + .subCategory(전자기기_서브_노트북_카테고리) + .startPrice(new Price(10_000)) + .bidUnit(new BidUnit(1_000)) + .closingTime(LocalDateTime.now().plusDays(10L)) + .build(); final Auction 낙찰자가_없는_경매 = Auction.builder() - .seller(판매자) - .title("맥북") - .description("맥북 팔아요") - .subCategory(전자기기_서브_노트북_카테고리) - .startPrice(new Price(10_000)) - .bidUnit(new BidUnit(1_000)) - .closingTime(LocalDateTime.now()) - .build(); + .seller(판매자) + .title("맥북") + .description("맥북 팔아요") + .subCategory(전자기기_서브_노트북_카테고리) + .startPrice(new Price(10_000)) + .bidUnit(new BidUnit(1_000)) + .closingTime(LocalDateTime.now()) + .build(); 판매자_엔초_구매자_지토_경매 = Auction.builder() - .seller(엔초) - .title("엔초 맥북") - .description("엔초 맥북 팔아요") - .subCategory(전자기기_서브_노트북_카테고리) - .startPrice(new Price(10_000)) - .bidUnit(new BidUnit(1_000)) - .closingTime(LocalDateTime.now()) - .build(); + .seller(엔초) + .title("엔초 맥북") + .description("엔초 맥북 팔아요") + .subCategory(전자기기_서브_노트북_카테고리) + .startPrice(new Price(10_000)) + .bidUnit(new BidUnit(1_000)) + .closingTime(LocalDateTime.now()) + .build(); final Auction 판매자_제이미_구매자_엔초_경매 = Auction.builder() - .seller(제이미) - .title("제이미 맥북") - .description("제이미 맥북 팔아요") - .subCategory(전자기기_서브_노트북_카테고리) - .startPrice(new Price(10_000)) - .bidUnit(new BidUnit(1_000)) - .closingTime(LocalDateTime.now()) - .build(); + .seller(제이미) + .title("제이미 맥북") + .description("제이미 맥북 팔아요") + .subCategory(전자기기_서브_노트북_카테고리) + .startPrice(new Price(10_000)) + .bidUnit(new BidUnit(1_000)) + .closingTime(LocalDateTime.now()) + .build(); 채팅방이_없는_경매.addAuctionImages(List.of(경매_대표_이미지, 대표_이미지가_아닌_경매_이미지)); 판매자_엔초_구매자_지토_경매.addAuctionImages(List.of(엔초의_경매_대표_이미지, 엔초의_대표_이미지가_아닌_경매_이미지)); 판매자_제이미_구매자_엔초_경매.addAuctionImages(List.of(제이미의_경매_대표_이미지, 제이미의_대표_이미지가_아닌_경매_이미지)); - auctionRepository.saveAll( - List.of(채팅방이_없는_경매, 종료되지_않은_경매, 낙찰자가_없는_경매, 판매자_엔초_구매자_지토_경매, 판매자_제이미_구매자_엔초_경매) - ); + + auctionRepository.save(채팅방이_없는_경매); + auctionRepository.save(종료되지_않은_경매); + auctionRepository.save(낙찰자가_없는_경매); + auctionRepository.save(판매자_엔초_구매자_지토_경매); + auctionRepository.save(판매자_제이미_구매자_엔초_경매); final Bid 채팅방_없는_경매_입찰 = new Bid(채팅방이_없는_경매, 구매자, new BidPrice(15_000)); final Bid 지토가_엔초_경매에_입찰 = new Bid(판매자_엔초_구매자_지토_경매, 지토, new BidPrice(15_000)); final Bid 엔초가_제이미_경매에_입찰 = new Bid(판매자_제이미_구매자_엔초_경매, 엔초, new BidPrice(15_000)); - bidRepository.saveAll(List.of(채팅방_없는_경매_입찰, 지토가_엔초_경매에_입찰, 엔초가_제이미_경매에_입찰)); + bidRepository.save(채팅방_없는_경매_입찰); + bidRepository.save(지토가_엔초_경매에_입찰); + bidRepository.save(엔초가_제이미_경매에_입찰); 채팅방이_없는_경매.updateLastBid(채팅방_없는_경매_입찰); 판매자_엔초_구매자_지토_경매.updateLastBid(지토가_엔초_경매에_입찰); 판매자_제이미_구매자_엔초_경매.updateLastBid(엔초가_제이미_경매에_입찰); 엔초_지토_채팅방 = new ChatRoom(판매자_엔초_구매자_지토_경매, 지토); 제이미_엔초_채팅방 = new ChatRoom(판매자_제이미_구매자_엔초_경매, 엔초); - chatRoomRepository.saveAll(List.of(엔초_지토_채팅방, 제이미_엔초_채팅방)); + + chatRoomRepository.save(엔초_지토_채팅방); + chatRoomRepository.save(제이미_엔초_채팅방); 엔초가_지토에게_1시에_보낸_쪽지 = Message.builder() - .chatRoom(엔초_지토_채팅방) - .contents("엔초가 지토에게 1시애 보낸 쪽지") - .writer(엔초) - .receiver(지토) - .build(); + .chatRoom(엔초_지토_채팅방) + .contents("엔초가 지토에게 1시애 보낸 쪽지") + .writer(엔초) + .receiver(지토) + .build(); 제이미가_엔초에게_2시에_보낸_쪽지 = Message.builder() - .chatRoom(제이미_엔초_채팅방) - .contents("제이미가 엔초에게 2시애 보낸 쪽지") - .writer(제이미) - .receiver(엔초) - .build(); - messageRepository.saveAll(List.of(엔초가_지토에게_1시에_보낸_쪽지, 제이미가_엔초에게_2시에_보낸_쪽지)); + .chatRoom(제이미_엔초_채팅방) + .contents("제이미가 엔초에게 2시애 보낸 쪽지") + .writer(제이미) + .receiver(엔초) + .build(); + messageRepository.save(엔초가_지토에게_1시에_보낸_쪽지); + messageRepository.save(제이미가_엔초에게_2시에_보낸_쪽지); final ChatRoomAndImageDto 엔초_지토_채팅방_정보 = new ChatRoomAndImageDto(엔초_지토_채팅방, 엔초의_경매_대표_이미지); 엔초_회원_정보 = new AuthenticationUserInfo(엔초.getId()); @@ -233,6 +247,7 @@ void setUp() { 엔초_지토_채팅방_정보_및_참여_가능 = new ReadChatRoomDto(엔초_지토_채팅방.getId(), true); 엔초_지토_채팅방_정보_및_참여_불가능 = new ReadChatRoomDto(엔초_지토_채팅방.getId(), false); 채팅방은_아직_없지만_참여_가능 = new ReadChatRoomDto(null, true); + 채팅방_없고_참여_불가능 = new ReadChatRoomDto(null, false); 엔초_채팅_목록의_제이미_엔초_채팅방_정보 = new ReadChatRoomWithLastMessageDto( 제이미_엔초_채팅방.getId(), ReadAuctionInChatRoomDto.of(판매자_제이미_구매자_엔초_경매, 제이미의_경매_대표_이미지), diff --git a/backend/ddang/src/test/java/com/ddang/ddang/chat/application/fixture/MessageServiceFixture.java b/backend/ddang/src/test/java/com/ddang/ddang/chat/application/fixture/MessageServiceFixture.java index 05e417e85..892695e39 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/chat/application/fixture/MessageServiceFixture.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/chat/application/fixture/MessageServiceFixture.java @@ -3,19 +3,19 @@ import com.ddang.ddang.auction.domain.Auction; import com.ddang.ddang.auction.domain.BidUnit; import com.ddang.ddang.auction.domain.Price; -import com.ddang.ddang.auction.infrastructure.persistence.JpaAuctionRepository; +import com.ddang.ddang.auction.domain.repository.AuctionRepository; import com.ddang.ddang.category.domain.Category; import com.ddang.ddang.category.infrastructure.persistence.JpaCategoryRepository; import com.ddang.ddang.chat.application.dto.CreateMessageDto; import com.ddang.ddang.chat.domain.ChatRoom; import com.ddang.ddang.chat.domain.Message; -import com.ddang.ddang.chat.infrastructure.persistence.JpaChatRoomRepository; -import com.ddang.ddang.chat.infrastructure.persistence.JpaMessageRepository; +import com.ddang.ddang.chat.domain.repository.ChatRoomRepository; +import com.ddang.ddang.chat.domain.repository.MessageRepository; import com.ddang.ddang.chat.presentation.dto.request.ReadMessageRequest; import com.ddang.ddang.image.domain.ProfileImage; import com.ddang.ddang.user.domain.Reliability; import com.ddang.ddang.user.domain.User; -import com.ddang.ddang.user.infrastructure.persistence.JpaUserRepository; +import com.ddang.ddang.user.domain.repository.UserRepository; import org.junit.jupiter.api.BeforeEach; import org.springframework.beans.factory.annotation.Autowired; @@ -27,16 +27,16 @@ public class MessageServiceFixture { @Autowired - private JpaMessageRepository messageRepository; + private AuctionRepository auctionRepository; @Autowired - private JpaAuctionRepository auctionRepository; + private MessageRepository messageRepository; @Autowired - private JpaUserRepository userRepository; + private UserRepository userRepository; @Autowired - private JpaChatRoomRepository chatRoomRepository; + private ChatRoomRepository chatRoomRepository; @Autowired private JpaCategoryRepository categoryRepository; @@ -91,11 +91,15 @@ void setUp() { .oauthId("12347") .build(); 탈퇴한_사용자.withdrawal(); - userRepository.saveAll(List.of(발신자, 수신자, 탈퇴한_사용자)); + userRepository.save(발신자); + userRepository.save(수신자); + userRepository.save(탈퇴한_사용자); final ChatRoom 채팅방 = new ChatRoom(경매, 발신자); final ChatRoom 탈퇴한_사용자와의_채팅방 = new ChatRoom(경매, 탈퇴한_사용자); - chatRoomRepository.saveAll(List.of(채팅방, 탈퇴한_사용자와의_채팅방)); + + chatRoomRepository.save(채팅방); + chatRoomRepository.save(탈퇴한_사용자와의_채팅방); 메시지_생성_DTO = new CreateMessageDto( 채팅방.getId(), diff --git a/backend/ddang/src/test/java/com/ddang/ddang/chat/infrastructure/persistence/ChatRoomAndImageRepositoryImplTest.java b/backend/ddang/src/test/java/com/ddang/ddang/chat/infrastructure/persistence/ChatRoomAndImageRepositoryImplTest.java new file mode 100644 index 000000000..8a145065e --- /dev/null +++ b/backend/ddang/src/test/java/com/ddang/ddang/chat/infrastructure/persistence/ChatRoomAndImageRepositoryImplTest.java @@ -0,0 +1,45 @@ +package com.ddang.ddang.chat.infrastructure.persistence; + +import com.ddang.ddang.chat.domain.dto.ChatRoomAndImageDto; +import com.ddang.ddang.chat.domain.repository.ChatRoomAndImageRepository; +import com.ddang.ddang.chat.infrastructure.persistence.fixture.ChatRoomAndImageRepositoryImplFixture; +import com.ddang.ddang.configuration.JpaConfiguration; +import com.ddang.ddang.configuration.QuerydslConfiguration; +import com.querydsl.jpa.impl.JPAQueryFactory; +import org.assertj.core.api.SoftAssertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.context.annotation.Import; + +import java.util.Optional; + +@DataJpaTest +@Import({JpaConfiguration.class, QuerydslConfiguration.class}) +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) +@SuppressWarnings("NonAsciiCharacters") +class ChatRoomAndImageRepositoryImplTest extends ChatRoomAndImageRepositoryImplFixture { + + ChatRoomAndImageRepository chatRoomAndImageRepository; + + @BeforeEach + void setUp(@Autowired final JPAQueryFactory queryFactory) { + chatRoomAndImageRepository = new ChatRoomAndImageRepositoryImpl(new QuerydslChatRoomAndImageRepository(queryFactory)); + } + + @Test + void 지정한_채팅방_아이디에_해당하는_채팅방을_조회한다() { + // when + final Optional actual = chatRoomAndImageRepository.findChatRoomById(채팅방.getId()); + + // then + SoftAssertions.assertSoftly(softAssertions -> { + softAssertions.assertThat(actual).isPresent(); + softAssertions.assertThat(actual.get().chatRoom()).isEqualTo(채팅방); + softAssertions.assertThat(actual.get().thumbnailImage()).isEqualTo(경매_대표_이미지); + }); + } +} diff --git a/backend/ddang/src/test/java/com/ddang/ddang/chat/infrastructure/persistence/ChatRoomAndMessageAndImageRepositoryImplTest.java b/backend/ddang/src/test/java/com/ddang/ddang/chat/infrastructure/persistence/ChatRoomAndMessageAndImageRepositoryImplTest.java new file mode 100644 index 000000000..b93f53f7e --- /dev/null +++ b/backend/ddang/src/test/java/com/ddang/ddang/chat/infrastructure/persistence/ChatRoomAndMessageAndImageRepositoryImplTest.java @@ -0,0 +1,54 @@ +package com.ddang.ddang.chat.infrastructure.persistence; + +import com.ddang.ddang.chat.domain.dto.ChatRoomAndMessageAndImageDto; +import com.ddang.ddang.chat.domain.repository.ChatRoomAndMessageAndImageRepository; +import com.ddang.ddang.chat.infrastructure.persistence.fixture.ChatRoomAndMessageAndImageRepositoryImplFixture; +import com.ddang.ddang.configuration.JpaConfiguration; +import com.ddang.ddang.configuration.QuerydslConfiguration; +import com.querydsl.jpa.impl.JPAQueryFactory; +import org.assertj.core.api.SoftAssertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.context.annotation.Import; + +import java.util.List; + +@DataJpaTest +@Import({JpaConfiguration.class, QuerydslConfiguration.class}) +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) +@SuppressWarnings("NonAsciiCharacters") +class ChatRoomAndMessageAndImageRepositoryImplTest extends ChatRoomAndMessageAndImageRepositoryImplFixture { + + ChatRoomAndMessageAndImageRepository chatRoomAndMessageAndImageRepository; + + @BeforeEach + void setUp(@Autowired final JPAQueryFactory queryFactory) { + chatRoomAndMessageAndImageRepository = + new ChatRoomAndMessageAndImageRepositoryImpl(new QuerydslChatRoomAndMessageAndImageRepository(queryFactory)); + } + + @Test + void 지정한_사용자_아이디가_포함된_채팅방을_조회한다() { + // when + final List actual = + chatRoomAndMessageAndImageRepository.findAllChatRoomInfoByUserIdOrderByLastMessage(엔초.getId()); + + // then + SoftAssertions.assertSoftly(softAssertions -> { + softAssertions.assertThat(actual).hasSize(3); + softAssertions.assertThat(actual.get(0).chatRoom()).isEqualTo(엔초_지토_채팅방); + softAssertions.assertThat(actual.get(0).message()).isEqualTo(엔초가_지토에게_5시에_보낸_쪽지); + softAssertions.assertThat(actual.get(0).thumbnailImage()).isEqualTo(엔초의_경매_대표_이미지); + softAssertions.assertThat(actual.get(1).chatRoom()).isEqualTo(제이미_엔초_채팅방); + softAssertions.assertThat(actual.get(1).message()).isEqualTo(제이미가_엔초에게_4시에_보낸_쪽지); + softAssertions.assertThat(actual.get(1).thumbnailImage()).isEqualTo(제이미의_경매_대표_이미지); + softAssertions.assertThat(actual.get(2).chatRoom()).isEqualTo(메리_엔초_채팅방); + softAssertions.assertThat(actual.get(2).message()).isEqualTo(메리가_엔초에게_3시에_보낸_쪽지); + softAssertions.assertThat(actual.get(2).thumbnailImage()).isEqualTo(메리의_경매_대표_이미지); + }); + } +} diff --git a/backend/ddang/src/test/java/com/ddang/ddang/chat/infrastructure/persistence/ChatRoomRepositoryImplTest.java b/backend/ddang/src/test/java/com/ddang/ddang/chat/infrastructure/persistence/ChatRoomRepositoryImplTest.java new file mode 100644 index 000000000..33520e3e1 --- /dev/null +++ b/backend/ddang/src/test/java/com/ddang/ddang/chat/infrastructure/persistence/ChatRoomRepositoryImplTest.java @@ -0,0 +1,81 @@ +package com.ddang.ddang.chat.infrastructure.persistence; + +import com.ddang.ddang.chat.domain.ChatRoom; +import com.ddang.ddang.chat.domain.repository.ChatRoomRepository; +import com.ddang.ddang.chat.infrastructure.persistence.fixture.ChatRoomRepositoryImplFixture; +import com.ddang.ddang.configuration.JpaConfiguration; +import com.ddang.ddang.configuration.QuerydslConfiguration; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.context.annotation.Import; + +import java.util.Optional; + +import static org.assertj.core.api.Assertions.assertThat; + +@DataJpaTest +@Import({JpaConfiguration.class, QuerydslConfiguration.class}) +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) +@SuppressWarnings("NonAsciiCharacters") +class ChatRoomRepositoryImplTest extends ChatRoomRepositoryImplFixture { + + ChatRoomRepository chatRoomRepository; + + @BeforeEach + void setUp(@Autowired final JpaChatRoomRepository jpaChatRoomRepository) { + chatRoomRepository = new ChatRoomRepositoryImpl(jpaChatRoomRepository); + } + + @Test + void 채팅방을_저장한다() { + // given + final ChatRoom chatRoom = new ChatRoom(경매, 구매자); + + // when + chatRoomRepository.save(chatRoom); + + // then + assertThat(chatRoom.getId()).isPositive(); + } + + @Test + void 지정한_아이디에_대한_채팅방을_조회한다() { + // when + final Optional actual = chatRoomRepository.findById(채팅방.getId()); + + // then + assertThat(actual).contains(채팅방); + } + + @Test + void 지정한_경매_아이디가_포함된_채팅방의_아이디를_조회한다() { + // when + final Optional actual = chatRoomRepository.findChatRoomIdByAuctionId(경매.getId()); + + // then + assertThat(actual).contains(채팅방.getId()); + } + + @Test + void 지정한_경매_아이디가_포함된_채팅방이_존재한다면_참을_반환한다() { + // when + final boolean actual = chatRoomRepository.existsByAuctionId(경매.getId()); + + // then + assertThat(actual).isTrue(); + } + + @Test + void 지정한_경매_아이디가_포함된_채팅방이_존재하지_않는다면_거짓을_반환한다() { + // when + final boolean actual = chatRoomRepository.existsByAuctionId(존재하지_않는_채팅방_아이디); + + // then + assertThat(actual).isFalse(); + } + +} diff --git a/backend/ddang/src/test/java/com/ddang/ddang/chat/infrastructure/persistence/JpaChatRoomRepositoryTest.java b/backend/ddang/src/test/java/com/ddang/ddang/chat/infrastructure/persistence/JpaChatRoomRepositoryTest.java index 074ef8d34..18e656f27 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/chat/infrastructure/persistence/JpaChatRoomRepositoryTest.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/chat/infrastructure/persistence/JpaChatRoomRepositoryTest.java @@ -23,11 +23,8 @@ @SuppressWarnings("NonAsciiCharacters") class JpaChatRoomRepositoryTest extends JpaChatRoomRepositoryFixture { - @PersistenceContext - EntityManager em; - @Autowired - JpaChatRoomRepository chatRoomRepository; + JpaChatRoomRepository jpaChatRoomRepository; @Test void 채팅방을_저장한다() { @@ -35,28 +32,34 @@ class JpaChatRoomRepositoryTest extends JpaChatRoomRepositoryFixture { final ChatRoom chatRoom = new ChatRoom(경매, 구매자); // when - chatRoomRepository.save(chatRoom); + jpaChatRoomRepository.save(chatRoom); // then - em.flush(); - em.clear(); - assertThat(chatRoom.getId()).isPositive(); } @Test void 지정한_아이디에_대한_채팅방을_조회한다() { // when - final Optional actual = chatRoomRepository.findById(채팅방.getId()); + final Optional actual = jpaChatRoomRepository.findById(채팅방.getId()); // then assertThat(actual).contains(채팅방); } + @Test + void 지정한_경매_아이디가_포함된_채팅방의_아이디를_조회한다() { + // when + final Optional actual = jpaChatRoomRepository.findChatRoomIdByAuctionId(경매.getId()); + + // then + assertThat(actual).contains(채팅방.getId()); + } + @Test void 지정한_경매_아이디가_포함된_채팅방이_존재한다면_참을_반환한다() { // when - final boolean actual = chatRoomRepository.existsByAuctionId(경매.getId()); + final boolean actual = jpaChatRoomRepository.existsByAuctionId(경매.getId()); // then assertThat(actual).isTrue(); @@ -65,7 +68,7 @@ class JpaChatRoomRepositoryTest extends JpaChatRoomRepositoryFixture { @Test void 지정한_경매_아이디가_포함된_채팅방이_존재하지_않는다면_거짓을_반환한다() { // when - final boolean actual = chatRoomRepository.existsByAuctionId(존재하지_않는_채팅방_아이디); + final boolean actual = jpaChatRoomRepository.existsByAuctionId(존재하지_않는_채팅방_아이디); // then assertThat(actual).isFalse(); diff --git a/backend/ddang/src/test/java/com/ddang/ddang/chat/infrastructure/persistence/JpaMessageRepositoryTest.java b/backend/ddang/src/test/java/com/ddang/ddang/chat/infrastructure/persistence/JpaMessageRepositoryTest.java index c6e8cb278..0cb97b3e3 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/chat/infrastructure/persistence/JpaMessageRepositoryTest.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/chat/infrastructure/persistence/JpaMessageRepositoryTest.java @@ -21,9 +21,6 @@ @SuppressWarnings("NonAsciiCharacters") class JpaMessageRepositoryTest extends JpaMessageRepositoryFixture { - @PersistenceContext - EntityManager em; - @Autowired JpaMessageRepository messageRepository; @@ -32,9 +29,6 @@ class JpaMessageRepositoryTest extends JpaMessageRepositoryFixture { // when final Message actual = messageRepository.save(메시지); - em.flush(); - em.clear(); - // then assertThat(actual.getId()).isPositive(); } diff --git a/backend/ddang/src/test/java/com/ddang/ddang/chat/infrastructure/persistence/MessageRepositoryImplTest.java b/backend/ddang/src/test/java/com/ddang/ddang/chat/infrastructure/persistence/MessageRepositoryImplTest.java new file mode 100644 index 000000000..adef04b97 --- /dev/null +++ b/backend/ddang/src/test/java/com/ddang/ddang/chat/infrastructure/persistence/MessageRepositoryImplTest.java @@ -0,0 +1,75 @@ +package com.ddang.ddang.chat.infrastructure.persistence; + +import com.ddang.ddang.chat.domain.Message; +import com.ddang.ddang.chat.domain.repository.MessageRepository; +import com.ddang.ddang.chat.infrastructure.persistence.fixture.MessageRepositoryImplFixture; +import com.ddang.ddang.configuration.JpaConfiguration; +import com.ddang.ddang.configuration.QuerydslConfiguration; +import com.querydsl.jpa.impl.JPAQueryFactory; +import org.assertj.core.api.SoftAssertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.context.annotation.Import; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +@DataJpaTest +@Import({JpaConfiguration.class, QuerydslConfiguration.class}) +@SuppressWarnings("NonAsciiCharacters") +public class MessageRepositoryImplTest extends MessageRepositoryImplFixture { + + MessageRepository messageRepository; + + @BeforeEach + void setUp( + @Autowired final JPAQueryFactory queryFactory, + @Autowired final JpaMessageRepository jpaMessageRepository + ) { + messageRepository = new MessageRepositoryImpl(jpaMessageRepository, new QuerydslMessageRepository(queryFactory)); + } + + @Test + void 메시지를_저장한다() { + // when + messageRepository.save(저장할_메시지); + + // then + assertThat(저장할_메시지.getId()).isPositive(); + } + + @Test + void 메시지_아이디에_해당하는_메시지가_존재하면_true를_반환한다() { + // when + final boolean actual = messageRepository.existsById(저장된_메시지.getId()); + + // then + assertThat(actual).isTrue(); + } + + @Test + void 메시지_아이디에_해당하는_메시지가_존재하지_않으면_false를_반환한다() { + // when + final boolean actual = messageRepository.existsById(존재하지_않는_메시지_아이디); + + // then + assertThat(actual).isFalse(); + } + + @Test + void 마지막_메시지_이후에_저장된_모든_메시지를_조회한다() { + // when + final List actual = messageRepository.findAllByLastMessageId(구매자_겸_수신자.getId(), 메시지가_5개인_채팅방.getId(), 두_번째_메시지_아이디); + + // then + SoftAssertions.assertSoftly(softAssertions -> { + softAssertions.assertThat(actual).hasSize(3); + softAssertions.assertThat(actual.get(0)).isEqualTo(세_번째_메시지); + softAssertions.assertThat(actual.get(1)).isEqualTo(네_번째_메시지); + softAssertions.assertThat(actual.get(2)).isEqualTo(다섯_번째_메시지); + }); + } +} diff --git a/backend/ddang/src/test/java/com/ddang/ddang/chat/infrastructure/persistence/QuerydslChatRoomAndImageRepositoryImplTest.java b/backend/ddang/src/test/java/com/ddang/ddang/chat/infrastructure/persistence/QuerydslChatRoomAndImageRepositoryTest.java similarity index 86% rename from backend/ddang/src/test/java/com/ddang/ddang/chat/infrastructure/persistence/QuerydslChatRoomAndImageRepositoryImplTest.java rename to backend/ddang/src/test/java/com/ddang/ddang/chat/infrastructure/persistence/QuerydslChatRoomAndImageRepositoryTest.java index 052546a56..9000193fc 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/chat/infrastructure/persistence/QuerydslChatRoomAndImageRepositoryImplTest.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/chat/infrastructure/persistence/QuerydslChatRoomAndImageRepositoryTest.java @@ -1,7 +1,7 @@ package com.ddang.ddang.chat.infrastructure.persistence; -import com.ddang.ddang.chat.infrastructure.persistence.dto.ChatRoomAndImageDto; -import com.ddang.ddang.chat.infrastructure.persistence.fixture.QuerydslChatRoomAndImageRepositoryImplFixture; +import com.ddang.ddang.chat.domain.dto.ChatRoomAndImageDto; +import com.ddang.ddang.chat.infrastructure.persistence.fixture.QuerydslChatRoomAndImageRepositoryFixture; import com.ddang.ddang.configuration.JpaConfiguration; import com.ddang.ddang.configuration.QuerydslConfiguration; import com.querydsl.jpa.impl.JPAQueryFactory; @@ -20,13 +20,13 @@ @Import({JpaConfiguration.class, QuerydslConfiguration.class}) @DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) @SuppressWarnings("NonAsciiCharacters") -class QuerydslChatRoomAndImageRepositoryImplTest extends QuerydslChatRoomAndImageRepositoryImplFixture { +class QuerydslChatRoomAndImageRepositoryTest extends QuerydslChatRoomAndImageRepositoryFixture { QuerydslChatRoomAndImageRepository querydslChatRoomAndImageRepository; @BeforeEach void setUp(@Autowired final JPAQueryFactory queryFactory) { - querydslChatRoomAndImageRepository = new QuerydslChatRoomAndImageRepositoryImpl(queryFactory); + querydslChatRoomAndImageRepository = new QuerydslChatRoomAndImageRepository(queryFactory); } @Test diff --git a/backend/ddang/src/test/java/com/ddang/ddang/chat/infrastructure/persistence/QuerydslChatRoomAndMessageAndImageRepositoryImplTest.java b/backend/ddang/src/test/java/com/ddang/ddang/chat/infrastructure/persistence/QuerydslChatRoomAndMessageAndImageRepositoryTest.java similarity index 88% rename from backend/ddang/src/test/java/com/ddang/ddang/chat/infrastructure/persistence/QuerydslChatRoomAndMessageAndImageRepositoryImplTest.java rename to backend/ddang/src/test/java/com/ddang/ddang/chat/infrastructure/persistence/QuerydslChatRoomAndMessageAndImageRepositoryTest.java index ed1804b90..694cb6999 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/chat/infrastructure/persistence/QuerydslChatRoomAndMessageAndImageRepositoryImplTest.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/chat/infrastructure/persistence/QuerydslChatRoomAndMessageAndImageRepositoryTest.java @@ -1,7 +1,7 @@ package com.ddang.ddang.chat.infrastructure.persistence; -import com.ddang.ddang.chat.infrastructure.persistence.dto.ChatRoomAndMessageAndImageDto; -import com.ddang.ddang.chat.infrastructure.persistence.fixture.QuerydslChatRoomAndMessageAndImageRepositoryImplFixture; +import com.ddang.ddang.chat.domain.dto.ChatRoomAndMessageAndImageDto; +import com.ddang.ddang.chat.infrastructure.persistence.fixture.QuerydslChatRoomAndMessageAndImageRepositoryFixture; import com.ddang.ddang.configuration.JpaConfiguration; import com.ddang.ddang.configuration.QuerydslConfiguration; import com.querydsl.jpa.impl.JPAQueryFactory; @@ -20,13 +20,13 @@ @Import({JpaConfiguration.class, QuerydslConfiguration.class}) @DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) @SuppressWarnings("NonAsciiCharacters") -class QuerydslChatRoomAndMessageAndImageRepositoryImplTest extends QuerydslChatRoomAndMessageAndImageRepositoryImplFixture { +class QuerydslChatRoomAndMessageAndImageRepositoryTest extends QuerydslChatRoomAndMessageAndImageRepositoryFixture { QuerydslChatRoomAndMessageAndImageRepository querydslChatRoomAndMessageAndImageRepository; @BeforeEach void setUp(@Autowired final JPAQueryFactory queryFactory) { - querydslChatRoomAndMessageAndImageRepository = new QuerydslChatRoomAndMessageAndImageRepositoryImpl(queryFactory); + querydslChatRoomAndMessageAndImageRepository = new QuerydslChatRoomAndMessageAndImageRepository(queryFactory); } @Test diff --git a/backend/ddang/src/test/java/com/ddang/ddang/chat/infrastructure/persistence/QuerydslChatRoomRepositoryImplTest.java b/backend/ddang/src/test/java/com/ddang/ddang/chat/infrastructure/persistence/QuerydslChatRoomRepositoryImplTest.java deleted file mode 100644 index b9bf60cd8..000000000 --- a/backend/ddang/src/test/java/com/ddang/ddang/chat/infrastructure/persistence/QuerydslChatRoomRepositoryImplTest.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.ddang.ddang.chat.infrastructure.persistence; - -import com.ddang.ddang.chat.infrastructure.persistence.fixture.QuerydslChatRoomRepositoryImplFixture; -import com.ddang.ddang.configuration.JpaConfiguration; -import com.ddang.ddang.configuration.QuerydslConfiguration; -import org.junit.jupiter.api.DisplayNameGeneration; -import org.junit.jupiter.api.DisplayNameGenerator; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; -import org.springframework.context.annotation.Import; - -import java.util.Optional; - -import static org.assertj.core.api.Assertions.assertThat; - -@DataJpaTest -@Import({JpaConfiguration.class, QuerydslConfiguration.class}) -@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) -@SuppressWarnings("NonAsciiCharacters") -class QuerydslChatRoomRepositoryImplTest extends QuerydslChatRoomRepositoryImplFixture { - - @Autowired - JpaChatRoomRepository chatRoomRepository; - - @Test - void 지정한_경매_아이디가_포함된_채팅방의_아이디를_조회한다() { - // when - final Optional actual = chatRoomRepository.findChatRoomIdByAuctionId(경매.getId()); - - // then - assertThat(actual).contains(채팅방.getId()); - } -} diff --git a/backend/ddang/src/test/java/com/ddang/ddang/chat/infrastructure/persistence/QuerydslMessageRepositoryImplTest.java b/backend/ddang/src/test/java/com/ddang/ddang/chat/infrastructure/persistence/QuerydslMessageRepositoryTest.java similarity index 91% rename from backend/ddang/src/test/java/com/ddang/ddang/chat/infrastructure/persistence/QuerydslMessageRepositoryImplTest.java rename to backend/ddang/src/test/java/com/ddang/ddang/chat/infrastructure/persistence/QuerydslMessageRepositoryTest.java index bcf573dc7..2c29b2691 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/chat/infrastructure/persistence/QuerydslMessageRepositoryImplTest.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/chat/infrastructure/persistence/QuerydslMessageRepositoryTest.java @@ -1,7 +1,7 @@ package com.ddang.ddang.chat.infrastructure.persistence; import com.ddang.ddang.chat.domain.Message; -import com.ddang.ddang.chat.infrastructure.persistence.fixture.QuerydslMessageRepositoryImplFixture; +import com.ddang.ddang.chat.infrastructure.persistence.fixture.QuerydslMessageRepositoryFixture; import com.ddang.ddang.configuration.JpaConfiguration; import com.ddang.ddang.configuration.QuerydslConfiguration; import com.querydsl.jpa.impl.JPAQueryFactory; @@ -22,19 +22,19 @@ @DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) @SuppressWarnings("NonAsciiCharacters") @Import({JpaConfiguration.class, QuerydslConfiguration.class}) -class QuerydslMessageRepositoryImplTest extends QuerydslMessageRepositoryImplFixture { +class QuerydslMessageRepositoryTest extends QuerydslMessageRepositoryFixture { - QuerydslMessageRepositoryImpl querydslMessageRepository; + QuerydslMessageRepository querydslMessageRepository; @BeforeEach - void setUp(@Autowired final JPAQueryFactory queryFactory) { - querydslMessageRepository = new QuerydslMessageRepositoryImpl(queryFactory); + void setUp(@Autowired final JPAQueryFactory jpaQueryFactory) { + querydslMessageRepository = new QuerydslMessageRepository(jpaQueryFactory); } @Test void 마지막으로_읽은_메시지_이후에_추가된_메시지를_조회한다() { // when - final List actual = querydslMessageRepository.findMessagesAllByLastMessageId( + final List actual = querydslMessageRepository.findAllByLastMessageId( 판매자.getId(), 채팅방.getId(), 세_번째_메시지.getId() @@ -56,7 +56,7 @@ void setUp(@Autowired final JPAQueryFactory queryFactory) { @Test void 상대방이_메시지를_추가한_경우_마지막으로_읽은_메시지_이후의_메시지를_조회한다() { // when - final List actual = querydslMessageRepository.findMessagesAllByLastMessageId( + final List actual = querydslMessageRepository.findAllByLastMessageId( 구매자.getId(), 채팅방.getId(), 세_번째_메시지.getId() @@ -78,7 +78,7 @@ void setUp(@Autowired final JPAQueryFactory queryFactory) { @Test void 마지막으로_읽은_메시지_이후의_메시지가_없는_경우_빈_리스트를_반환한다() { // when - final List actual = querydslMessageRepository.findMessagesAllByLastMessageId( + final List actual = querydslMessageRepository.findAllByLastMessageId( 구매자.getId(), 채팅방.getId(), 열_번째_메시지.getId() diff --git a/backend/ddang/src/test/java/com/ddang/ddang/chat/infrastructure/persistence/fixture/ChatRoomAndImageRepositoryImplFixture.java b/backend/ddang/src/test/java/com/ddang/ddang/chat/infrastructure/persistence/fixture/ChatRoomAndImageRepositoryImplFixture.java new file mode 100644 index 000000000..651a16e8d --- /dev/null +++ b/backend/ddang/src/test/java/com/ddang/ddang/chat/infrastructure/persistence/fixture/ChatRoomAndImageRepositoryImplFixture.java @@ -0,0 +1,108 @@ +package com.ddang.ddang.chat.infrastructure.persistence.fixture; + +import com.ddang.ddang.auction.domain.Auction; +import com.ddang.ddang.auction.domain.BidUnit; +import com.ddang.ddang.auction.domain.Price; +import com.ddang.ddang.auction.domain.repository.AuctionRepository; +import com.ddang.ddang.auction.infrastructure.persistence.AuctionRepositoryImpl; +import com.ddang.ddang.auction.infrastructure.persistence.JpaAuctionRepository; +import com.ddang.ddang.auction.infrastructure.persistence.QuerydslAuctionRepository; +import com.ddang.ddang.bid.domain.Bid; +import com.ddang.ddang.bid.domain.BidPrice; +import com.ddang.ddang.bid.domain.repository.BidRepository; +import com.ddang.ddang.bid.infrastructure.persistence.BidRepositoryImpl; +import com.ddang.ddang.bid.infrastructure.persistence.JpaBidRepository; +import com.ddang.ddang.category.domain.Category; +import com.ddang.ddang.category.infrastructure.persistence.JpaCategoryRepository; +import com.ddang.ddang.chat.domain.ChatRoom; +import com.ddang.ddang.chat.domain.repository.ChatRoomRepository; +import com.ddang.ddang.chat.infrastructure.persistence.ChatRoomRepositoryImpl; +import com.ddang.ddang.chat.infrastructure.persistence.JpaChatRoomRepository; +import com.ddang.ddang.image.domain.AuctionImage; +import com.ddang.ddang.image.domain.ProfileImage; +import com.ddang.ddang.user.domain.Reliability; +import com.ddang.ddang.user.domain.User; +import com.ddang.ddang.user.domain.repository.UserRepository; +import com.ddang.ddang.user.infrastructure.persistence.JpaUserRepository; +import com.ddang.ddang.user.infrastructure.persistence.UserRepositoryImpl; +import com.querydsl.jpa.impl.JPAQueryFactory; +import org.junit.jupiter.api.BeforeEach; +import org.springframework.beans.factory.annotation.Autowired; + +import java.time.LocalDateTime; +import java.util.List; + +@SuppressWarnings("NonAsciiCharacters") +public class ChatRoomAndImageRepositoryImplFixture { + + @Autowired + private JpaCategoryRepository categoryRepository; + + private AuctionRepository auctionRepository; + + private UserRepository userRepository; + + private BidRepository bidRepository; + + private ChatRoomRepository chatRoomRepository; + + protected AuctionImage 경매_대표_이미지; + protected ChatRoom 채팅방; + + @BeforeEach + void fixtureSetUp( + @Autowired final JPAQueryFactory jpaQueryFactory, + @Autowired final JpaAuctionRepository jpaAuctionRepository, + @Autowired final JpaUserRepository jpaUserRepository, + @Autowired final JpaChatRoomRepository jpaChatRoomRepository, + @Autowired final JpaBidRepository jpaBidRepository + ) { + auctionRepository = new AuctionRepositoryImpl(jpaAuctionRepository, new QuerydslAuctionRepository(jpaQueryFactory)); + userRepository = new UserRepositoryImpl(jpaUserRepository); + chatRoomRepository = new ChatRoomRepositoryImpl(jpaChatRoomRepository); + bidRepository = new BidRepositoryImpl(jpaBidRepository); + + final Category 전자기기_카테고리 = new Category("전자기기"); + final Category 전자기기_서브_노트북_카테고리 = new Category("노트북 카테고리"); + final ProfileImage 프로필_이미지 = new ProfileImage("upload.png", "store.png"); + final User 판매자 = User.builder() + .name("판매자") + .profileImage(프로필_이미지) + .reliability(new Reliability(4.7d)) + .oauthId("12345") + .build(); + final User 구매자 = User.builder() + .name("구매자") + .profileImage(프로필_이미지) + .reliability(new Reliability(4.7d)) + .oauthId("12346") + .build(); + final AuctionImage 대표_이미지가_아닌_경매_이미지 = + new AuctionImage("대표 이미지가_아닌_경매_이미지.png", "대표 이미지가_아닌_경매_이미지.png"); + final Auction 경매 = Auction.builder() + .seller(판매자) + .title("맥북") + .description("맥북 팔아요") + .subCategory(전자기기_서브_노트북_카테고리) + .startPrice(new Price(10_000)) + .bidUnit(new BidUnit(1_000)) + .closingTime(LocalDateTime.now()) + .build(); + final Bid 입찰 = new Bid(경매, 구매자, new BidPrice(15_000)); + + 경매_대표_이미지 = new AuctionImage("경매_대표_이미지.png", "경매_대표_이미지.png"); + 채팅방 = new ChatRoom(경매, 구매자); + auctionRepository.save(경매); + + 전자기기_카테고리.addSubCategory(전자기기_서브_노트북_카테고리); + categoryRepository.save(전자기기_카테고리); + + userRepository.save(판매자); + userRepository.save(구매자); + + 경매.addAuctionImages(List.of(경매_대표_이미지, 대표_이미지가_아닌_경매_이미지)); + bidRepository.save(입찰); + 경매.updateLastBid(입찰); + chatRoomRepository.save(채팅방); + } +} diff --git a/backend/ddang/src/test/java/com/ddang/ddang/chat/infrastructure/persistence/fixture/ChatRoomAndMessageAndImageRepositoryImplFixture.java b/backend/ddang/src/test/java/com/ddang/ddang/chat/infrastructure/persistence/fixture/ChatRoomAndMessageAndImageRepositoryImplFixture.java new file mode 100644 index 000000000..79bdacb98 --- /dev/null +++ b/backend/ddang/src/test/java/com/ddang/ddang/chat/infrastructure/persistence/fixture/ChatRoomAndMessageAndImageRepositoryImplFixture.java @@ -0,0 +1,222 @@ +package com.ddang.ddang.chat.infrastructure.persistence.fixture; + +import com.ddang.ddang.auction.domain.Auction; +import com.ddang.ddang.auction.domain.BidUnit; +import com.ddang.ddang.auction.domain.Price; +import com.ddang.ddang.auction.domain.repository.AuctionRepository; +import com.ddang.ddang.auction.infrastructure.persistence.AuctionRepositoryImpl; +import com.ddang.ddang.auction.infrastructure.persistence.JpaAuctionRepository; +import com.ddang.ddang.auction.infrastructure.persistence.QuerydslAuctionRepository; +import com.ddang.ddang.bid.domain.Bid; +import com.ddang.ddang.bid.domain.BidPrice; +import com.ddang.ddang.bid.domain.repository.BidRepository; +import com.ddang.ddang.bid.infrastructure.persistence.BidRepositoryImpl; +import com.ddang.ddang.bid.infrastructure.persistence.JpaBidRepository; +import com.ddang.ddang.category.domain.Category; +import com.ddang.ddang.category.infrastructure.persistence.JpaCategoryRepository; +import com.ddang.ddang.chat.domain.ChatRoom; +import com.ddang.ddang.chat.domain.Message; +import com.ddang.ddang.chat.domain.repository.ChatRoomRepository; +import com.ddang.ddang.chat.domain.repository.MessageRepository; +import com.ddang.ddang.chat.infrastructure.persistence.ChatRoomRepositoryImpl; +import com.ddang.ddang.chat.infrastructure.persistence.JpaChatRoomRepository; +import com.ddang.ddang.chat.infrastructure.persistence.JpaMessageRepository; +import com.ddang.ddang.chat.infrastructure.persistence.MessageRepositoryImpl; +import com.ddang.ddang.chat.infrastructure.persistence.QuerydslMessageRepository; +import com.ddang.ddang.image.domain.AuctionImage; +import com.ddang.ddang.image.domain.ProfileImage; +import com.ddang.ddang.user.domain.Reliability; +import com.ddang.ddang.user.domain.User; +import com.ddang.ddang.user.domain.repository.UserRepository; +import com.ddang.ddang.user.infrastructure.persistence.JpaUserRepository; +import com.ddang.ddang.user.infrastructure.persistence.UserRepositoryImpl; +import com.querydsl.jpa.impl.JPAQueryFactory; +import org.junit.jupiter.api.BeforeEach; +import org.springframework.beans.factory.annotation.Autowired; + +import java.time.LocalDateTime; +import java.util.List; + +@SuppressWarnings("NonAsciiCharacters") +public class ChatRoomAndMessageAndImageRepositoryImplFixture { + + @Autowired + private JpaCategoryRepository categoryRepository; + + private AuctionRepository auctionRepository; + + private UserRepository userRepository; + + private BidRepository bidRepository; + + private ChatRoomRepository chatRoomRepository; + + private MessageRepository messageRepository; + + protected User 엔초; + protected AuctionImage 메리의_경매_대표_이미지; + protected AuctionImage 엔초의_경매_대표_이미지; + protected AuctionImage 제이미의_경매_대표_이미지; + protected ChatRoom 메리_엔초_채팅방; + protected ChatRoom 엔초_지토_채팅방; + protected ChatRoom 제이미_엔초_채팅방; + protected Message 메리가_엔초에게_3시에_보낸_쪽지; + protected Message 제이미가_엔초에게_4시에_보낸_쪽지; + protected Message 엔초가_지토에게_5시에_보낸_쪽지; + + @BeforeEach + void fixtureSetUp( + @Autowired final JPAQueryFactory jpaQueryFactory, + @Autowired final JpaAuctionRepository jpaAuctionRepository, + @Autowired final JpaUserRepository jpaUserRepository, + @Autowired final JpaChatRoomRepository jpaChatRoomRepository, + @Autowired final JpaBidRepository jpaBidRepository, + @Autowired final JpaMessageRepository jpaMessageRepository + ) { + auctionRepository = new AuctionRepositoryImpl(jpaAuctionRepository, new QuerydslAuctionRepository(jpaQueryFactory)); + userRepository = new UserRepositoryImpl(jpaUserRepository); + chatRoomRepository = new ChatRoomRepositoryImpl(jpaChatRoomRepository); + bidRepository = new BidRepositoryImpl(jpaBidRepository); + messageRepository = new MessageRepositoryImpl(jpaMessageRepository, new QuerydslMessageRepository(jpaQueryFactory)); + + final Category 전자기기_카테고리 = new Category("전자기기"); + final Category 전자기기_서브_노트북_카테고리 = new Category("노트북 카테고리"); + final ProfileImage 프로필_이미지 = new ProfileImage("upload.png", "store.png"); + + 엔초 = User.builder() + .name("엔초") + .profileImage(프로필_이미지) + .reliability(new Reliability(4.7d)) + .oauthId("12346") + .build(); + final User 메리 = User.builder() + .name("메리") + .profileImage(프로필_이미지) + .reliability(new Reliability(4.7d)) + .oauthId("12345") + .build(); + final User 제이미 = User.builder() + .name("제이미") + .profileImage(프로필_이미지) + .reliability(new Reliability(4.7d)) + .oauthId("12347") + .build(); + final User 지토 = User.builder() + .name("지토") + .profileImage(프로필_이미지) + .reliability(new Reliability(4.7d)) + .oauthId("12348") + .build(); + + 메리의_경매_대표_이미지 = new AuctionImage("메리의_경매_대표_이미지.png", "메리의_경매_대표_이미지.png"); + final AuctionImage 메리의_대표_이미지가_아닌_경매_이미지 = + new AuctionImage("메리의_대표 이미지가_아닌_경매_이미지.png", "메리의_대표 이미지가_아닌_경매_이미지.png"); + 엔초의_경매_대표_이미지 = new AuctionImage("엔초의_경매_대표_이미지.png", "엔초의_경매_대표_이미지.png"); + final AuctionImage 엔초의_대표_이미지가_아닌_경매_이미지 = + new AuctionImage("엔초의_대표 이미지가_아닌_경매_이미지.png", "엔초의_대표 이미지가_아닌_경매_이미지.png"); + 제이미의_경매_대표_이미지 = new AuctionImage("제이미의_경매_대표_이미지.png", "제이미의_경매_대표_이미지.png"); + final AuctionImage 제이미의_대표_이미지가_아닌_경매_이미지 = + new AuctionImage("제이미의_대표 이미지가_아닌_경매_이미지.png", "제이미의_대표 이미지가_아닌_경매_이미지.png"); + + final Auction 메리의_경매 = Auction.builder() + .seller(메리) + .title("메리 맥북") + .description("메리 맥북 팔아요") + .subCategory(전자기기_서브_노트북_카테고리) + .startPrice(new Price(10_000)) + .bidUnit(new BidUnit(1_000)) + .closingTime(LocalDateTime.now()) + .build(); + final Auction 엔초의_경매 = Auction.builder() + .seller(엔초) + .title("엔초 맥북") + .description("엔초 맥북 팔아요") + .subCategory(전자기기_서브_노트북_카테고리) + .startPrice(new Price(10_000)) + .bidUnit(new BidUnit(1_000)) + .closingTime(LocalDateTime.now()) + .build(); + final Auction 제이미의_경매 = Auction.builder() + .seller(제이미) + .title("제이미 맥북") + .description("제이미 맥북 팔아요") + .subCategory(전자기기_서브_노트북_카테고리) + .startPrice(new Price(10_000)) + .bidUnit(new BidUnit(1_000)) + .closingTime(LocalDateTime.now()) + .build(); + + final Bid 엔초가_메리_경매에_입찰 = new Bid(메리의_경매, 엔초, new BidPrice(15_000)); + final Bid 지토가_엔초_경매에_입찰 = new Bid(엔초의_경매, 지토, new BidPrice(15_000)); + final Bid 엔초가_제이미_경매에_입찰 = new Bid(제이미의_경매, 엔초, new BidPrice(15_000)); + + 메리_엔초_채팅방 = new ChatRoom(메리의_경매, 엔초); + 엔초_지토_채팅방 = new ChatRoom(엔초의_경매, 지토); + 제이미_엔초_채팅방 = new ChatRoom(제이미의_경매, 엔초); + + final Message 제이미가_엔초에게_1시에_보낸_쪽지 = Message.builder() + .chatRoom(제이미_엔초_채팅방) + .contents("제이미가 엔초에게 1시애 보낸 쪽지") + .writer(제이미) + .receiver(엔초) + .build(); + final Message 엔초가_지토에게_2시에_보낸_쪽지 = Message.builder() + .chatRoom(엔초_지토_채팅방) + .contents("엔초가 지토에게 2시애 보낸 쪽지") + .writer(엔초) + .receiver(지토) + .build(); + 메리가_엔초에게_3시에_보낸_쪽지 = Message.builder() + .chatRoom(메리_엔초_채팅방) + .contents("메리가 엔초에게 3시에 보낸 쪽지") + .writer(엔초) + .receiver(지토) + .build(); + 제이미가_엔초에게_4시에_보낸_쪽지 = Message.builder() + .chatRoom(제이미_엔초_채팅방) + .contents("제이미가 엔초에게 4시애 보낸 쪽지") + .writer(제이미) + .receiver(엔초) + .build(); + 엔초가_지토에게_5시에_보낸_쪽지 = Message.builder() + .chatRoom(엔초_지토_채팅방) + .contents("엔초가 지토에게 5시애 보낸 쪽지") + .writer(엔초) + .receiver(지토) + .build(); + + + 전자기기_카테고리.addSubCategory(전자기기_서브_노트북_카테고리); + categoryRepository.save(전자기기_카테고리); + + userRepository.save(메리); + userRepository.save(엔초); + userRepository.save(제이미); + userRepository.save(지토); + + 메리의_경매.addAuctionImages(List.of(메리의_경매_대표_이미지, 메리의_대표_이미지가_아닌_경매_이미지)); + 엔초의_경매.addAuctionImages(List.of(엔초의_경매_대표_이미지, 엔초의_대표_이미지가_아닌_경매_이미지)); + 제이미의_경매.addAuctionImages(List.of(제이미의_경매_대표_이미지, 제이미의_대표_이미지가_아닌_경매_이미지)); + + auctionRepository.save(메리의_경매); + auctionRepository.save(엔초의_경매); + auctionRepository.save(제이미의_경매); + + bidRepository.save(엔초가_메리_경매에_입찰); + bidRepository.save(지토가_엔초_경매에_입찰); + bidRepository.save(엔초가_제이미_경매에_입찰); + 메리의_경매.updateLastBid(엔초가_메리_경매에_입찰); + 엔초의_경매.updateLastBid(지토가_엔초_경매에_입찰); + 제이미의_경매.updateLastBid(엔초가_제이미_경매에_입찰); + + chatRoomRepository.save(메리_엔초_채팅방); + chatRoomRepository.save(엔초_지토_채팅방); + chatRoomRepository.save(제이미_엔초_채팅방); + + messageRepository.save(제이미가_엔초에게_1시에_보낸_쪽지); + messageRepository.save(엔초가_지토에게_2시에_보낸_쪽지); + messageRepository.save(메리가_엔초에게_3시에_보낸_쪽지); + messageRepository.save(제이미가_엔초에게_4시에_보낸_쪽지); + messageRepository.save(엔초가_지토에게_5시에_보낸_쪽지); + } +} diff --git a/backend/ddang/src/test/java/com/ddang/ddang/chat/infrastructure/persistence/fixture/ChatRoomRepositoryImplFixture.java b/backend/ddang/src/test/java/com/ddang/ddang/chat/infrastructure/persistence/fixture/ChatRoomRepositoryImplFixture.java new file mode 100644 index 000000000..826573910 --- /dev/null +++ b/backend/ddang/src/test/java/com/ddang/ddang/chat/infrastructure/persistence/fixture/ChatRoomRepositoryImplFixture.java @@ -0,0 +1,115 @@ +package com.ddang.ddang.chat.infrastructure.persistence.fixture; + +import com.ddang.ddang.auction.domain.Auction; +import com.ddang.ddang.auction.domain.BidUnit; +import com.ddang.ddang.auction.domain.Price; +import com.ddang.ddang.auction.domain.repository.AuctionRepository; +import com.ddang.ddang.auction.infrastructure.persistence.AuctionRepositoryImpl; +import com.ddang.ddang.auction.infrastructure.persistence.JpaAuctionRepository; +import com.ddang.ddang.auction.infrastructure.persistence.QuerydslAuctionRepository; +import com.ddang.ddang.bid.domain.Bid; +import com.ddang.ddang.bid.domain.BidPrice; +import com.ddang.ddang.bid.domain.repository.BidRepository; +import com.ddang.ddang.bid.infrastructure.persistence.BidRepositoryImpl; +import com.ddang.ddang.bid.infrastructure.persistence.JpaBidRepository; +import com.ddang.ddang.category.domain.Category; +import com.ddang.ddang.category.infrastructure.persistence.JpaCategoryRepository; +import com.ddang.ddang.chat.domain.ChatRoom; +import com.ddang.ddang.chat.domain.repository.ChatRoomRepository; +import com.ddang.ddang.chat.infrastructure.persistence.ChatRoomRepositoryImpl; +import com.ddang.ddang.chat.infrastructure.persistence.JpaChatRoomRepository; +import com.ddang.ddang.image.domain.AuctionImage; +import com.ddang.ddang.image.domain.ProfileImage; +import com.ddang.ddang.user.domain.Reliability; +import com.ddang.ddang.user.domain.User; +import com.ddang.ddang.user.domain.repository.UserRepository; +import com.ddang.ddang.user.infrastructure.persistence.JpaUserRepository; +import com.ddang.ddang.user.infrastructure.persistence.UserRepositoryImpl; +import com.querydsl.jpa.impl.JPAQueryFactory; +import org.junit.jupiter.api.BeforeEach; +import org.springframework.beans.factory.annotation.Autowired; + +import java.time.LocalDateTime; +import java.util.List; + +@SuppressWarnings("NonAsciiCharacters") +public class ChatRoomRepositoryImplFixture { + + @Autowired + private JpaCategoryRepository categoryRepository; + + private UserRepository userRepository; + + private AuctionRepository auctionRepository; + + private BidRepository bidRepository; + + private ChatRoomRepository chatRoomRepository; + + protected User 판매자; + protected User 구매자; + protected Auction 경매; + private Bid 입찰; + protected ChatRoom 채팅방; + protected Long 존재하지_않는_채팅방_아이디 = -999L; + + @BeforeEach + void fixtureSetUp( + @Autowired final JPAQueryFactory jpaQueryFactory, + @Autowired final JpaAuctionRepository jpaAuctionRepository, + @Autowired final JpaUserRepository jpaUserRepository, + @Autowired final JpaChatRoomRepository jpaChatRoomRepository, + @Autowired final JpaBidRepository jpaBidRepository + ) { + auctionRepository = new AuctionRepositoryImpl(jpaAuctionRepository, new QuerydslAuctionRepository(jpaQueryFactory)); + userRepository = new UserRepositoryImpl(jpaUserRepository); + chatRoomRepository = new ChatRoomRepositoryImpl(jpaChatRoomRepository); + bidRepository = new BidRepositoryImpl(jpaBidRepository); + + final Category 전자기기_카테고리 = new Category("전자기기"); + final Category 전자기기_서브_노트북_카테고리 = new Category("노트북 카테고리"); + final ProfileImage 프로필_이미지 = new ProfileImage("upload.png", "store.png"); + final AuctionImage 경매이미지1 = new AuctionImage("경매이미지1.png", "경매이미지1.png"); + final AuctionImage 경매이미지2 = new AuctionImage("경매이미지2.png", "경매이미지2.png"); + + 판매자 = User.builder() + .name("판매자") + .profileImage(프로필_이미지) + .reliability(new Reliability(4.7d)) + .oauthId("12345") + .build(); + 구매자 = User.builder() + .name("구매자") + .profileImage(프로필_이미지) + .reliability(new Reliability(4.7d)) + .oauthId("12346") + .build(); + 경매 = Auction.builder() + .seller(판매자) + .title("맥북") + .description("맥북 팔아요") + .subCategory(전자기기_서브_노트북_카테고리) + .startPrice(new Price(10_000)) + .bidUnit(new BidUnit(1_000)) + .closingTime(LocalDateTime.now()) + .build(); + + 입찰 = new Bid(경매, 구매자, new BidPrice(15_000)); + + 채팅방 = new ChatRoom(경매, 구매자); + + 전자기기_카테고리.addSubCategory(전자기기_서브_노트북_카테고리); + categoryRepository.save(전자기기_카테고리); + + userRepository.save(판매자); + userRepository.save(구매자); + + 경매.addAuctionImages(List.of(경매이미지1, 경매이미지2)); + auctionRepository.save(경매); + + bidRepository.save(입찰); + 경매.updateLastBid(입찰); + + chatRoomRepository.save(채팅방); + } +} diff --git a/backend/ddang/src/test/java/com/ddang/ddang/chat/infrastructure/persistence/fixture/MessageRepositoryImplFixture.java b/backend/ddang/src/test/java/com/ddang/ddang/chat/infrastructure/persistence/fixture/MessageRepositoryImplFixture.java new file mode 100644 index 000000000..105997c3d --- /dev/null +++ b/backend/ddang/src/test/java/com/ddang/ddang/chat/infrastructure/persistence/fixture/MessageRepositoryImplFixture.java @@ -0,0 +1,128 @@ +package com.ddang.ddang.chat.infrastructure.persistence.fixture; + +import com.ddang.ddang.auction.domain.Auction; +import com.ddang.ddang.auction.domain.repository.AuctionRepository; +import com.ddang.ddang.auction.infrastructure.persistence.AuctionRepositoryImpl; +import com.ddang.ddang.auction.infrastructure.persistence.JpaAuctionRepository; +import com.ddang.ddang.auction.infrastructure.persistence.QuerydslAuctionRepository; +import com.ddang.ddang.chat.domain.ChatRoom; +import com.ddang.ddang.chat.domain.Message; +import com.ddang.ddang.chat.domain.repository.ChatRoomRepository; +import com.ddang.ddang.chat.domain.repository.MessageRepository; +import com.ddang.ddang.chat.infrastructure.persistence.ChatRoomRepositoryImpl; +import com.ddang.ddang.chat.infrastructure.persistence.JpaChatRoomRepository; +import com.ddang.ddang.chat.infrastructure.persistence.JpaMessageRepository; +import com.ddang.ddang.chat.infrastructure.persistence.MessageRepositoryImpl; +import com.ddang.ddang.chat.infrastructure.persistence.QuerydslMessageRepository; +import com.ddang.ddang.image.domain.ProfileImage; +import com.ddang.ddang.user.domain.Reliability; +import com.ddang.ddang.user.domain.User; +import com.ddang.ddang.user.domain.repository.UserRepository; +import com.ddang.ddang.user.infrastructure.persistence.JpaUserRepository; +import com.ddang.ddang.user.infrastructure.persistence.UserRepositoryImpl; +import com.querydsl.jpa.impl.JPAQueryFactory; +import org.junit.jupiter.api.BeforeEach; +import org.springframework.beans.factory.annotation.Autowired; + +import java.util.ArrayList; +import java.util.List; + +@SuppressWarnings("NonAsciiCharacters") +public class MessageRepositoryImplFixture { + + private MessageRepository messageRepository; + private UserRepository userRepository; + private AuctionRepository auctionRepository; + private ChatRoomRepository chatRoomRepository; + + protected User 판매자_겸_발신자; + protected User 구매자_겸_수신자; + protected Auction 경매; + protected Auction 메시지가_5개인_경매; + protected ChatRoom 채팅방; + protected ChatRoom 메시지가_5개인_채팅방; + protected Message 저장할_메시지; + protected Message 저장된_메시지; + protected Message 세_번째_메시지; + protected Message 네_번째_메시지; + protected Message 다섯_번째_메시지; + protected long 두_번째_메시지_아이디; + + protected String 메시지_내용 = "메시지 내용"; + protected long 존재하지_않는_메시지_아이디 = -999L; + + @BeforeEach + void fixtureSetUp( + @Autowired final JPAQueryFactory queryFactory, + @Autowired final JpaMessageRepository jpaMessageRepository, + @Autowired final JpaUserRepository jpaUserRepository, + @Autowired final JpaAuctionRepository jpaAuctionRepository, + @Autowired final JpaChatRoomRepository jpaChatRoomRepository + ) { + messageRepository = new MessageRepositoryImpl(jpaMessageRepository, new QuerydslMessageRepository(queryFactory)); + userRepository = new UserRepositoryImpl(jpaUserRepository); + auctionRepository = new AuctionRepositoryImpl(jpaAuctionRepository, new QuerydslAuctionRepository((queryFactory))); + chatRoomRepository = new ChatRoomRepositoryImpl(jpaChatRoomRepository); + + 판매자_겸_발신자 = User.builder() + .name("판매자") + .profileImage(new ProfileImage("upload.png", "store.png")) + .reliability(new Reliability(4.7d)) + .oauthId("78923") + .build(); + 구매자_겸_수신자 = User.builder() + .name("구매자") + .profileImage(new ProfileImage("upload.png", "store.png")) + .reliability(new Reliability(4.7d)) + .oauthId("12345") + .build(); + userRepository.save(판매자_겸_발신자); + userRepository.save(구매자_겸_수신자); + + 경매 = Auction.builder() + .title("title") + .build(); + 메시지가_5개인_경매 = Auction.builder() + .title("메시지가_5개인_경매") + .build(); + auctionRepository.save(경매); + auctionRepository.save(메시지가_5개인_경매); + + 채팅방 = new ChatRoom(경매, 구매자_겸_수신자); + 메시지가_5개인_채팅방 = new ChatRoom(메시지가_5개인_경매, 구매자_겸_수신자); + chatRoomRepository.save(채팅방); + chatRoomRepository.save(메시지가_5개인_채팅방); + + 저장할_메시지 = Message.builder() + .chatRoom(채팅방) + .writer(판매자_겸_발신자) + .receiver(구매자_겸_수신자) + .contents(메시지_내용) + .build(); + 저장된_메시지 = Message.builder() + .chatRoom(채팅방) + .writer(판매자_겸_발신자) + .receiver(구매자_겸_수신자) + .contents("레포지토리에 저장된 메시지") + .build(); + messageRepository.save(저장된_메시지); + + int 메시지_총_개수 = 5; + List 저장된_메시지들 = new ArrayList<>(); + for (int count = 0; count < 메시지_총_개수; count++) { + final Message 메시지 = Message.builder() + .chatRoom(메시지가_5개인_채팅방) + .writer(판매자_겸_발신자) + .receiver(구매자_겸_수신자) + .contents("안녕하세요") + .build(); + 저장된_메시지들.add(메시지); + messageRepository.save(메시지); + } + + 두_번째_메시지_아이디 = 저장된_메시지들.get(1).getId(); + 세_번째_메시지 = 저장된_메시지들.get(2); + 네_번째_메시지 = 저장된_메시지들.get(3); + 다섯_번째_메시지 = 저장된_메시지들.get(4); + } +} diff --git a/backend/ddang/src/test/java/com/ddang/ddang/chat/infrastructure/persistence/fixture/QuerydslChatRoomAndImageRepositoryImplFixture.java b/backend/ddang/src/test/java/com/ddang/ddang/chat/infrastructure/persistence/fixture/QuerydslChatRoomAndImageRepositoryFixture.java similarity index 86% rename from backend/ddang/src/test/java/com/ddang/ddang/chat/infrastructure/persistence/fixture/QuerydslChatRoomAndImageRepositoryImplFixture.java rename to backend/ddang/src/test/java/com/ddang/ddang/chat/infrastructure/persistence/fixture/QuerydslChatRoomAndImageRepositoryFixture.java index 15c437d93..8285b0a5c 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/chat/infrastructure/persistence/fixture/QuerydslChatRoomAndImageRepositoryImplFixture.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/chat/infrastructure/persistence/fixture/QuerydslChatRoomAndImageRepositoryFixture.java @@ -3,7 +3,10 @@ import com.ddang.ddang.auction.domain.Auction; import com.ddang.ddang.auction.domain.BidUnit; import com.ddang.ddang.auction.domain.Price; +import com.ddang.ddang.auction.domain.repository.AuctionRepository; +import com.ddang.ddang.auction.infrastructure.persistence.AuctionRepositoryImpl; import com.ddang.ddang.auction.infrastructure.persistence.JpaAuctionRepository; +import com.ddang.ddang.auction.infrastructure.persistence.QuerydslAuctionRepository; import com.ddang.ddang.bid.domain.Bid; import com.ddang.ddang.bid.domain.BidPrice; import com.ddang.ddang.bid.infrastructure.persistence.JpaBidRepository; @@ -16,6 +19,7 @@ import com.ddang.ddang.user.domain.Reliability; import com.ddang.ddang.user.domain.User; import com.ddang.ddang.user.infrastructure.persistence.JpaUserRepository; +import com.querydsl.jpa.impl.JPAQueryFactory; import jakarta.persistence.EntityManager; import jakarta.persistence.PersistenceContext; import org.junit.jupiter.api.BeforeEach; @@ -25,19 +29,22 @@ import java.util.List; @SuppressWarnings("NonAsciiCharacters") -public class QuerydslChatRoomAndImageRepositoryImplFixture { +public class QuerydslChatRoomAndImageRepositoryFixture { @PersistenceContext private EntityManager em; @Autowired - private JpaCategoryRepository categoryRepository; + private JPAQueryFactory queryFactory; @Autowired - private JpaUserRepository userRepository; + private JpaAuctionRepository jpaAuctionRepository; @Autowired - private JpaAuctionRepository auctionRepository; + private JpaCategoryRepository categoryRepository; + + @Autowired + private JpaUserRepository userRepository; @Autowired private JpaBidRepository bidRepository; @@ -81,11 +88,16 @@ void setUp() { 경매_대표_이미지 = new AuctionImage("경매_대표_이미지.png", "경매_대표_이미지.png"); 채팅방 = new ChatRoom(경매, 구매자); + final AuctionRepository auctionRepository = new AuctionRepositoryImpl( + jpaAuctionRepository, + new QuerydslAuctionRepository(queryFactory) + ); + auctionRepository.save(경매); + 전자기기_카테고리.addSubCategory(전자기기_서브_노트북_카테고리); categoryRepository.save(전자기기_카테고리); userRepository.saveAll(List.of(판매자, 구매자)); 경매.addAuctionImages(List.of(경매_대표_이미지, 대표_이미지가_아닌_경매_이미지)); - auctionRepository.save(경매); bidRepository.save(입찰); 경매.updateLastBid(입찰); chatRoomRepository.save(채팅방); diff --git a/backend/ddang/src/test/java/com/ddang/ddang/chat/infrastructure/persistence/fixture/QuerydslChatRoomAndMessageAndImageRepositoryImplFixture.java b/backend/ddang/src/test/java/com/ddang/ddang/chat/infrastructure/persistence/fixture/QuerydslChatRoomAndMessageAndImageRepositoryFixture.java similarity index 93% rename from backend/ddang/src/test/java/com/ddang/ddang/chat/infrastructure/persistence/fixture/QuerydslChatRoomAndMessageAndImageRepositoryImplFixture.java rename to backend/ddang/src/test/java/com/ddang/ddang/chat/infrastructure/persistence/fixture/QuerydslChatRoomAndMessageAndImageRepositoryFixture.java index e404c5de1..49bdf7e18 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/chat/infrastructure/persistence/fixture/QuerydslChatRoomAndMessageAndImageRepositoryImplFixture.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/chat/infrastructure/persistence/fixture/QuerydslChatRoomAndMessageAndImageRepositoryFixture.java @@ -3,7 +3,10 @@ import com.ddang.ddang.auction.domain.Auction; import com.ddang.ddang.auction.domain.BidUnit; import com.ddang.ddang.auction.domain.Price; +import com.ddang.ddang.auction.domain.repository.AuctionRepository; +import com.ddang.ddang.auction.infrastructure.persistence.AuctionRepositoryImpl; import com.ddang.ddang.auction.infrastructure.persistence.JpaAuctionRepository; +import com.ddang.ddang.auction.infrastructure.persistence.QuerydslAuctionRepository; import com.ddang.ddang.bid.domain.Bid; import com.ddang.ddang.bid.domain.BidPrice; import com.ddang.ddang.bid.infrastructure.persistence.JpaBidRepository; @@ -18,6 +21,7 @@ import com.ddang.ddang.user.domain.Reliability; import com.ddang.ddang.user.domain.User; import com.ddang.ddang.user.infrastructure.persistence.JpaUserRepository; +import com.querydsl.jpa.impl.JPAQueryFactory; import jakarta.persistence.EntityManager; import jakarta.persistence.PersistenceContext; import org.junit.jupiter.api.BeforeEach; @@ -27,19 +31,22 @@ import java.util.List; @SuppressWarnings("NonAsciiCharacters") -public class QuerydslChatRoomAndMessageAndImageRepositoryImplFixture { +public class QuerydslChatRoomAndMessageAndImageRepositoryFixture { @PersistenceContext private EntityManager em; @Autowired - private JpaCategoryRepository categoryRepository; + private JPAQueryFactory queryFactory; @Autowired - private JpaUserRepository userRepository; + private JpaAuctionRepository jpaAuctionRepository; @Autowired - private JpaAuctionRepository auctionRepository; + private JpaCategoryRepository categoryRepository; + + @Autowired + private JpaUserRepository userRepository; @Autowired private JpaBidRepository bidRepository; @@ -178,7 +185,15 @@ void setUp() { 메리의_경매.addAuctionImages(List.of(메리의_경매_대표_이미지, 메리의_대표_이미지가_아닌_경매_이미지)); 엔초의_경매.addAuctionImages(List.of(엔초의_경매_대표_이미지, 엔초의_대표_이미지가_아닌_경매_이미지)); 제이미의_경매.addAuctionImages(List.of(제이미의_경매_대표_이미지, 제이미의_대표_이미지가_아닌_경매_이미지)); - auctionRepository.saveAll(List.of(메리의_경매, 엔초의_경매, 제이미의_경매)); + + final AuctionRepository auctionRepository = new AuctionRepositoryImpl( + jpaAuctionRepository, + new QuerydslAuctionRepository(queryFactory) + ); + + auctionRepository.save(메리의_경매); + auctionRepository.save(엔초의_경매); + auctionRepository.save(제이미의_경매); bidRepository.saveAll(List.of(엔초가_메리_경매에_입찰, 지토가_엔초_경매에_입찰, 엔초가_제이미_경매에_입찰)); 메리의_경매.updateLastBid(엔초가_메리_경매에_입찰); diff --git a/backend/ddang/src/test/java/com/ddang/ddang/chat/infrastructure/persistence/fixture/QuerydslChatRoomRepositoryImplFixture.java b/backend/ddang/src/test/java/com/ddang/ddang/chat/infrastructure/persistence/fixture/QuerydslChatRoomRepositoryImplFixture.java deleted file mode 100644 index bef8380e9..000000000 --- a/backend/ddang/src/test/java/com/ddang/ddang/chat/infrastructure/persistence/fixture/QuerydslChatRoomRepositoryImplFixture.java +++ /dev/null @@ -1,103 +0,0 @@ -package com.ddang.ddang.chat.infrastructure.persistence.fixture; - -import com.ddang.ddang.auction.domain.Auction; -import com.ddang.ddang.auction.domain.BidUnit; -import com.ddang.ddang.auction.domain.Price; -import com.ddang.ddang.auction.infrastructure.persistence.JpaAuctionRepository; -import com.ddang.ddang.bid.domain.Bid; -import com.ddang.ddang.bid.domain.BidPrice; -import com.ddang.ddang.bid.infrastructure.persistence.JpaBidRepository; -import com.ddang.ddang.category.domain.Category; -import com.ddang.ddang.category.infrastructure.persistence.JpaCategoryRepository; -import com.ddang.ddang.chat.domain.ChatRoom; -import com.ddang.ddang.chat.infrastructure.persistence.JpaChatRoomRepository; -import com.ddang.ddang.image.domain.AuctionImage; -import com.ddang.ddang.image.domain.ProfileImage; -import com.ddang.ddang.user.domain.Reliability; -import com.ddang.ddang.user.domain.User; -import com.ddang.ddang.user.infrastructure.persistence.JpaUserRepository; -import jakarta.persistence.EntityManager; -import jakarta.persistence.PersistenceContext; -import org.junit.jupiter.api.BeforeEach; -import org.springframework.beans.factory.annotation.Autowired; - -import java.time.LocalDateTime; -import java.util.List; - -@SuppressWarnings("NonAsciiCharacters") -public class QuerydslChatRoomRepositoryImplFixture { - - @PersistenceContext - private EntityManager em; - - @Autowired - private JpaCategoryRepository categoryRepository; - - @Autowired - private JpaUserRepository userRepository; - - @Autowired - private JpaAuctionRepository auctionRepository; - - @Autowired - private JpaBidRepository bidRepository; - - @Autowired - private JpaChatRoomRepository chatRoomRepository; - - protected AuctionImage 경매_대표_이미지; - protected Auction 경매; - protected ChatRoom 채팅방; - - @BeforeEach - void setUp() { - final Category 전자기기_카테고리 = new Category("전자기기"); - final Category 전자기기_서브_노트북_카테고리 = new Category("노트북 카테고리"); - final ProfileImage 프로필_이미지 = new ProfileImage("upload.png", "store.png"); - final User 판매자 = User.builder() - .name("판매자") - .profileImage(프로필_이미지) - .reliability(new Reliability(4.7d)) - .oauthId("12345") - .build(); - final User 구매자 = User.builder() - .name("구매자") - .profileImage(프로필_이미지) - .reliability(new Reliability(4.7d)) - .oauthId("12346") - .build(); - final AuctionImage 대표_이미지가_아닌_경매_이미지 = - new AuctionImage("대표 이미지가_아닌_경매_이미지.png", "대표 이미지가_아닌_경매_이미지.png"); - - 경매 = Auction.builder() - .seller(판매자) - .title("맥북") - .description("맥북 팔아요") - .subCategory(전자기기_서브_노트북_카테고리) - .startPrice(new Price(10_000)) - .bidUnit(new BidUnit(1_000)) - .closingTime(LocalDateTime.now()) - .build(); - - final Bid 입찰 = new Bid(경매, 구매자, new BidPrice(15_000)); - - 경매_대표_이미지 = new AuctionImage("경매_대표_이미지.png", "경매_대표_이미지.png"); - 채팅방 = new ChatRoom(경매, 구매자); - - 전자기기_카테고리.addSubCategory(전자기기_서브_노트북_카테고리); - categoryRepository.save(전자기기_카테고리); - - userRepository.saveAll(List.of(판매자, 구매자)); - - 경매.addAuctionImages(List.of(경매_대표_이미지, 대표_이미지가_아닌_경매_이미지)); - auctionRepository.save(경매); - - bidRepository.save(입찰); - 경매.updateLastBid(입찰); - - chatRoomRepository.save(채팅방); - - em.flush(); - em.clear(); - } -} diff --git a/backend/ddang/src/test/java/com/ddang/ddang/chat/infrastructure/persistence/fixture/QuerydslMessageRepositoryImplFixture.java b/backend/ddang/src/test/java/com/ddang/ddang/chat/infrastructure/persistence/fixture/QuerydslMessageRepositoryFixture.java similarity index 68% rename from backend/ddang/src/test/java/com/ddang/ddang/chat/infrastructure/persistence/fixture/QuerydslMessageRepositoryImplFixture.java rename to backend/ddang/src/test/java/com/ddang/ddang/chat/infrastructure/persistence/fixture/QuerydslMessageRepositoryFixture.java index fa02b5fb4..916829582 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/chat/infrastructure/persistence/fixture/QuerydslMessageRepositoryImplFixture.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/chat/infrastructure/persistence/fixture/QuerydslMessageRepositoryFixture.java @@ -1,15 +1,22 @@ package com.ddang.ddang.chat.infrastructure.persistence.fixture; import com.ddang.ddang.auction.domain.Auction; +import com.ddang.ddang.auction.domain.repository.AuctionRepository; +import com.ddang.ddang.auction.infrastructure.persistence.AuctionRepositoryImpl; import com.ddang.ddang.auction.infrastructure.persistence.JpaAuctionRepository; +import com.ddang.ddang.auction.infrastructure.persistence.QuerydslAuctionRepository; import com.ddang.ddang.chat.domain.ChatRoom; import com.ddang.ddang.chat.domain.Message; +import com.ddang.ddang.chat.domain.repository.MessageRepository; import com.ddang.ddang.chat.infrastructure.persistence.JpaChatRoomRepository; import com.ddang.ddang.chat.infrastructure.persistence.JpaMessageRepository; +import com.ddang.ddang.chat.infrastructure.persistence.MessageRepositoryImpl; +import com.ddang.ddang.chat.infrastructure.persistence.QuerydslMessageRepository; import com.ddang.ddang.image.domain.ProfileImage; import com.ddang.ddang.user.domain.Reliability; import com.ddang.ddang.user.domain.User; import com.ddang.ddang.user.infrastructure.persistence.JpaUserRepository; +import com.querydsl.jpa.impl.JPAQueryFactory; import jakarta.persistence.EntityManager; import jakarta.persistence.PersistenceContext; import org.junit.jupiter.api.BeforeEach; @@ -19,22 +26,25 @@ import java.util.List; @SuppressWarnings("NonAsciiCharacters") -public class QuerydslMessageRepositoryImplFixture { +public class QuerydslMessageRepositoryFixture { @PersistenceContext private EntityManager em; @Autowired - private JpaUserRepository userRepository; + private JPAQueryFactory queryFactory; @Autowired - private JpaAuctionRepository auctionRepository; + private JpaAuctionRepository jpaAuctionRepository; + + @Autowired + private JpaUserRepository userRepository; @Autowired private JpaChatRoomRepository chatRoomRepository; @Autowired - private JpaMessageRepository messageRepository; + private JpaMessageRepository jpaMessageRepository; protected User 판매자; protected User 구매자; @@ -53,6 +63,15 @@ public class QuerydslMessageRepositoryImplFixture { @BeforeEach void setUp() { + final AuctionRepository auctionRepository = new AuctionRepositoryImpl( + jpaAuctionRepository, + new QuerydslAuctionRepository(queryFactory) + ); + final MessageRepository messageRepository = new MessageRepositoryImpl( + jpaMessageRepository, + new QuerydslMessageRepository(queryFactory) + ); + 판매자 = User.builder() .name("판매자") .profileImage(new ProfileImage("upload.png", "store.png")) @@ -82,15 +101,15 @@ void setUp() { 메시지_총_개수 = 10; 저장된_메시지들 = new ArrayList<>(); for (int count = 0; count < 메시지_총_개수; count++) { - final Message message = Message.builder() - .chatRoom(채팅방) - .writer(판매자) - .receiver(구매자) - .contents("안녕하세요") - .build(); - 저장된_메시지들.add(message); + final Message 메시지 = Message.builder() + .chatRoom(채팅방) + .writer(판매자) + .receiver(구매자) + .contents("안녕하세요") + .build(); + 저장된_메시지들.add(메시지); + messageRepository.save(메시지); } - messageRepository.saveAll(저장된_메시지들); 세_번째_메시지 = 저장된_메시지들.get(2); 네_번째_메시지 = 저장된_메시지들.get(3); diff --git a/backend/ddang/src/test/java/com/ddang/ddang/device/application/DeviceTokenServiceTest.java b/backend/ddang/src/test/java/com/ddang/ddang/device/application/DeviceTokenServiceTest.java index 8d3b0e484..15187cd40 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/device/application/DeviceTokenServiceTest.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/device/application/DeviceTokenServiceTest.java @@ -3,7 +3,7 @@ import com.ddang.ddang.configuration.IsolateDatabase; import com.ddang.ddang.device.application.fixture.DeviceTokenServiceFixture; import com.ddang.ddang.device.domain.DeviceToken; -import com.ddang.ddang.device.infrastructure.persistence.JpaDeviceTokenRepository; +import com.ddang.ddang.device.domain.repository.DeviceTokenRepository; import com.ddang.ddang.user.application.exception.UserNotFoundException; import org.junit.jupiter.api.DisplayNameGeneration; import org.junit.jupiter.api.DisplayNameGenerator; @@ -25,7 +25,7 @@ class DeviceTokenServiceTest extends DeviceTokenServiceFixture { DeviceTokenService deviceTokenService; @Autowired - JpaDeviceTokenRepository deviceTokenRepository; + DeviceTokenRepository deviceTokenRepository; @Test void 사용자의_디바이스_토큰이_존재하지_않는다면_저장한다() { diff --git a/backend/ddang/src/test/java/com/ddang/ddang/device/application/fixture/DeviceTokenServiceFixture.java b/backend/ddang/src/test/java/com/ddang/ddang/device/application/fixture/DeviceTokenServiceFixture.java index 0006789d2..e7f5a01eb 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/device/application/fixture/DeviceTokenServiceFixture.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/device/application/fixture/DeviceTokenServiceFixture.java @@ -2,24 +2,23 @@ import com.ddang.ddang.device.application.dto.PersistDeviceTokenDto; import com.ddang.ddang.device.domain.DeviceToken; -import com.ddang.ddang.device.infrastructure.persistence.JpaDeviceTokenRepository; +import com.ddang.ddang.device.domain.repository.DeviceTokenRepository; import com.ddang.ddang.image.domain.ProfileImage; import com.ddang.ddang.user.domain.Reliability; import com.ddang.ddang.user.domain.User; -import com.ddang.ddang.user.infrastructure.persistence.JpaUserRepository; +import com.ddang.ddang.user.domain.repository.UserRepository; import org.junit.jupiter.api.BeforeEach; import org.springframework.beans.factory.annotation.Autowired; -import java.util.List; - @SuppressWarnings("NonAsciiCharacters") public class DeviceTokenServiceFixture { @Autowired - private JpaDeviceTokenRepository deviceTokenRepository; + private DeviceTokenRepository deviceTokenRepository; @Autowired - private JpaUserRepository userRepository; + private UserRepository userRepository; + private String 초기_디바이스_토큰_값 = "initialDeviceToken"; private DeviceToken 사용자의_디바이스_토큰; @@ -35,19 +34,22 @@ public class DeviceTokenServiceFixture { @BeforeEach void setUp() { 디바이스_토큰이_있는_사용자 = User.builder() - .name("디바이스 토큰이 있는 사용자") - .profileImage(new ProfileImage("upload.png", "store.png")) - .reliability(new Reliability(4.7d)) - .oauthId("12345") - .build(); + .name("디바이스 토큰이 있는 사용자") + .profileImage(new ProfileImage("upload.png", "store.png")) + .reliability(new Reliability(4.7d)) + .oauthId("12345") + .build(); 디바이스_토큰이_없는_사용자 = User.builder() - .name("디바이스 토큰이 없는 사용자") - .profileImage(new ProfileImage("upload.png", "store.png")) - .reliability(new Reliability(4.7d)) - .oauthId("12346") - .build(); + .name("디바이스 토큰이 없는 사용자") + .profileImage(new ProfileImage("upload.png", "store.png")) + .reliability(new Reliability(4.7d)) + .oauthId("12346") + .build(); 사용자의_디바이스_토큰 = new DeviceToken(디바이스_토큰이_있는_사용자, 사용_중인_디바이스_토큰_값); - userRepository.saveAll(List.of(디바이스_토큰이_있는_사용자, 디바이스_토큰이_없는_사용자)); + + userRepository.save(디바이스_토큰이_있는_사용자); + userRepository.save(디바이스_토큰이_없는_사용자); + deviceTokenRepository.save(사용자의_디바이스_토큰); 디바이스_토큰_저장을_위한_DTO = new PersistDeviceTokenDto(초기_디바이스_토큰_값); diff --git a/backend/ddang/src/test/java/com/ddang/ddang/device/infrastructure/persistence/DeviceTokenRepositoryImplTest.java b/backend/ddang/src/test/java/com/ddang/ddang/device/infrastructure/persistence/DeviceTokenRepositoryImplTest.java new file mode 100644 index 000000000..89b3f23de --- /dev/null +++ b/backend/ddang/src/test/java/com/ddang/ddang/device/infrastructure/persistence/DeviceTokenRepositoryImplTest.java @@ -0,0 +1,63 @@ +package com.ddang.ddang.device.infrastructure.persistence; + +import com.ddang.ddang.configuration.JpaConfiguration; +import com.ddang.ddang.configuration.QuerydslConfiguration; +import com.ddang.ddang.device.domain.DeviceToken; +import com.ddang.ddang.device.domain.repository.DeviceTokenRepository; +import com.ddang.ddang.device.infrastructure.persistence.fixture.DeviceTokenRepositoryImplFixture; +import org.assertj.core.api.SoftAssertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.context.annotation.Import; + +import java.util.Optional; + +import static org.assertj.core.api.Assertions.assertThat; + +@DataJpaTest +@Import({JpaConfiguration.class, QuerydslConfiguration.class}) +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) +@SuppressWarnings("NonAsciiCharacters") +public class DeviceTokenRepositoryImplTest extends DeviceTokenRepositoryImplFixture { + + DeviceTokenRepository deviceTokenRepository; + + @BeforeEach + void setUp(@Autowired final JpaDeviceTokenRepository jpaDeviceTokenRepository) { + deviceTokenRepository = new DeviceTokenRepositoryImpl(jpaDeviceTokenRepository); + } + + @Test + void 기기토큰을_저장한다() { + // when + deviceTokenRepository.save(아이디가_없는_기기토큰); + + // then + assertThat(아이디가_없는_기기토큰.getId()).isPositive(); + } + + @Test + void 사용자_아이디에_해당하는_기기토큰을_조회한다() { + // when + final Optional actual = deviceTokenRepository.findByUserId(아이디가_있는_기기토큰_사용자.getId()); + + // then + SoftAssertions.assertSoftly(softAssertions -> { + softAssertions.assertThat(actual).isPresent(); + softAssertions.assertThat(actual.get()).isEqualTo(아이디가_있는_기기토큰); + }); + } + + @Test + void 사용자_아이디에_해당하는_기기토큰을_삭제한다() { + // when + deviceTokenRepository.deleteByUserId(삭제할_기기토큰_사용자.getId()); + + // then + assertThat(deviceTokenRepository.findByUserId(삭제할_기기토큰_사용자.getId())).isEmpty(); + } +} diff --git a/backend/ddang/src/test/java/com/ddang/ddang/device/infrastructure/persistence/JpaDeviceTokenRepositoryTest.java b/backend/ddang/src/test/java/com/ddang/ddang/device/infrastructure/persistence/JpaDeviceTokenRepositoryTest.java index 06dbc3a7a..44df2f70f 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/device/infrastructure/persistence/JpaDeviceTokenRepositoryTest.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/device/infrastructure/persistence/JpaDeviceTokenRepositoryTest.java @@ -22,12 +22,12 @@ class JpaDeviceTokenRepositoryTest extends JpaDeviceTokenRepositoryFixture { @Autowired - JpaDeviceTokenRepository userDeviceTokenRepository; + JpaDeviceTokenRepository jpaDeviceTokenRepository; @Test void 주어진_사용자_아이디에_해당하는_기기토큰을_조회한다() { // when - final Optional actual = userDeviceTokenRepository.findByUserId(사용자.getId()); + final Optional actual = jpaDeviceTokenRepository.findByUserId(사용자.getId()); // then assertThat(actual).contains(사용자의_디바이스_토큰); diff --git a/backend/ddang/src/test/java/com/ddang/ddang/device/infrastructure/persistence/fixture/DeviceTokenRepositoryImplFixture.java b/backend/ddang/src/test/java/com/ddang/ddang/device/infrastructure/persistence/fixture/DeviceTokenRepositoryImplFixture.java new file mode 100644 index 000000000..d4e4e5728 --- /dev/null +++ b/backend/ddang/src/test/java/com/ddang/ddang/device/infrastructure/persistence/fixture/DeviceTokenRepositoryImplFixture.java @@ -0,0 +1,64 @@ +package com.ddang.ddang.device.infrastructure.persistence.fixture; + +import com.ddang.ddang.device.domain.DeviceToken; +import com.ddang.ddang.device.domain.repository.DeviceTokenRepository; +import com.ddang.ddang.device.infrastructure.persistence.DeviceTokenRepositoryImpl; +import com.ddang.ddang.device.infrastructure.persistence.JpaDeviceTokenRepository; +import com.ddang.ddang.image.domain.ProfileImage; +import com.ddang.ddang.user.domain.Reliability; +import com.ddang.ddang.user.domain.User; +import com.ddang.ddang.user.domain.repository.UserRepository; +import com.ddang.ddang.user.infrastructure.persistence.JpaUserRepository; +import com.ddang.ddang.user.infrastructure.persistence.UserRepositoryImpl; +import org.junit.jupiter.api.BeforeEach; +import org.springframework.beans.factory.annotation.Autowired; + +@SuppressWarnings("NonAsciiCharacters") +public class DeviceTokenRepositoryImplFixture { + + private UserRepository userRepository; + private DeviceTokenRepository deviceTokenRepository; + + protected DeviceToken 아이디가_없는_기기토큰; + protected DeviceToken 아이디가_있는_기기토큰; + protected DeviceToken 삭제할_기기토큰; + protected User 아이디가_있는_기기토큰_사용자; + protected User 삭제할_기기토큰_사용자; + + @BeforeEach + void fixtureSetUp( + @Autowired final JpaUserRepository jpaUserRepository, + @Autowired final JpaDeviceTokenRepository jpaDeviceTokenRepository + ) { + userRepository = new UserRepositoryImpl(jpaUserRepository); + deviceTokenRepository = new DeviceTokenRepositoryImpl(jpaDeviceTokenRepository); + + final User 아이디가_없는_기기토큰_사용자 = User.builder() + .name("저장하지 않은 기기토큰 사용자") + .profileImage(new ProfileImage("upload.png", "store.png")) + .reliability(new Reliability(4.7d)) + .oauthId("98765") + .build(); + 아이디가_있는_기기토큰_사용자 = User.builder() + .name("기기토큰_사용자") + .profileImage(new ProfileImage("upload.png", "store.png")) + .reliability(new Reliability(4.7d)) + .oauthId("12345") + .build(); + 삭제할_기기토큰_사용자 = User.builder() + .name("삭제할 기기토큰_사용자") + .profileImage(new ProfileImage("upload.png", "store.png")) + .reliability(new Reliability(4.7d)) + .oauthId("14392") + .build(); + userRepository.save(아이디가_없는_기기토큰_사용자); + userRepository.save(아이디가_있는_기기토큰_사용자); + userRepository.save(삭제할_기기토큰_사용자); + + 아이디가_없는_기기토큰 = new DeviceToken(아이디가_없는_기기토큰_사용자, "아이디가 없는 기기토큰"); + 아이디가_있는_기기토큰 = new DeviceToken(아이디가_있는_기기토큰_사용자, "아이디가_있는_기기토큰"); + 삭제할_기기토큰 = new DeviceToken(삭제할_기기토큰_사용자, "삭제할_기기토큰"); + deviceTokenRepository.save(아이디가_있는_기기토큰); + deviceTokenRepository.save(삭제할_기기토큰); + } +} diff --git a/backend/ddang/src/test/java/com/ddang/ddang/device/infrastructure/persistence/fixture/JpaDeviceTokenRepositoryFixture.java b/backend/ddang/src/test/java/com/ddang/ddang/device/infrastructure/persistence/fixture/JpaDeviceTokenRepositoryFixture.java index 128847999..1c578b432 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/device/infrastructure/persistence/fixture/JpaDeviceTokenRepositoryFixture.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/device/infrastructure/persistence/fixture/JpaDeviceTokenRepositoryFixture.java @@ -5,7 +5,9 @@ import com.ddang.ddang.image.domain.ProfileImage; import com.ddang.ddang.user.domain.Reliability; import com.ddang.ddang.user.domain.User; +import com.ddang.ddang.user.domain.repository.UserRepository; import com.ddang.ddang.user.infrastructure.persistence.JpaUserRepository; +import com.ddang.ddang.user.infrastructure.persistence.UserRepositoryImpl; import jakarta.persistence.EntityManager; import jakarta.persistence.PersistenceContext; import org.junit.jupiter.api.BeforeEach; @@ -17,28 +19,29 @@ public class JpaDeviceTokenRepositoryFixture { @PersistenceContext private EntityManager em; - @Autowired - private JpaUserRepository userRepository; + private UserRepository userRepository; @Autowired - private JpaDeviceTokenRepository deviceTokenRepository; + private JpaDeviceTokenRepository jpaDeviceTokenRepository; protected User 사용자; protected DeviceToken 사용자의_디바이스_토큰; @BeforeEach - void setUp() { + void setUp(@Autowired final JpaUserRepository jpaUserRepository) { + userRepository = new UserRepositoryImpl(jpaUserRepository); + 사용자 = User.builder() - .name("사용자") - .profileImage(new ProfileImage("upload.png", "store.png")) - .reliability(new Reliability(4.7d)) - .oauthId("12345") - .build(); + .name("사용자") + .profileImage(new ProfileImage("upload.png", "store.png")) + .reliability(new Reliability(4.7d)) + .oauthId("12345") + .build(); 사용자의_디바이스_토큰 = new DeviceToken(사용자, "deviceToken"); userRepository.save(사용자); - deviceTokenRepository.save(사용자의_디바이스_토큰); + jpaDeviceTokenRepository.save(사용자의_디바이스_토큰); em.flush(); em.clear(); diff --git a/backend/ddang/src/test/java/com/ddang/ddang/image/infrastructure/persistence/AuctionImageRepositoryImplTest.java b/backend/ddang/src/test/java/com/ddang/ddang/image/infrastructure/persistence/AuctionImageRepositoryImplTest.java new file mode 100644 index 000000000..a77fea90e --- /dev/null +++ b/backend/ddang/src/test/java/com/ddang/ddang/image/infrastructure/persistence/AuctionImageRepositoryImplTest.java @@ -0,0 +1,40 @@ +package com.ddang.ddang.image.infrastructure.persistence; + +import com.ddang.ddang.configuration.QuerydslConfiguration; +import com.ddang.ddang.image.domain.AuctionImage; +import com.ddang.ddang.image.domain.repository.AuctionImageRepository; +import com.ddang.ddang.image.infrastructure.persistence.fixture.AuctionImageRepositoryImplFixture; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.context.annotation.Import; + +import java.util.Optional; + +import static org.assertj.core.api.Assertions.assertThat; + +@DataJpaTest +@Import(QuerydslConfiguration.class) +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) +@SuppressWarnings("NonAsciiCharacters") +class AuctionImageRepositoryImplTest extends AuctionImageRepositoryImplFixture { + + AuctionImageRepository auctionImageRepository; + + @BeforeEach + void setUp(@Autowired final JpaAuctionImageRepository jpaAuctionImageRepository) { + auctionImageRepository = new AuctionImageRepositoryImpl(jpaAuctionImageRepository); + } + + @Test + void 경매_이미지를_아이디를_통해_조회한다() { + // when + final Optional actual = auctionImageRepository.findById(경매_이미지.getId()); + + // then + assertThat(actual).contains(경매_이미지); + } +} diff --git a/backend/ddang/src/test/java/com/ddang/ddang/image/infrastructure/persistence/ProfileImageRepositoryImplTest.java b/backend/ddang/src/test/java/com/ddang/ddang/image/infrastructure/persistence/ProfileImageRepositoryImplTest.java new file mode 100644 index 000000000..7f733ae97 --- /dev/null +++ b/backend/ddang/src/test/java/com/ddang/ddang/image/infrastructure/persistence/ProfileImageRepositoryImplTest.java @@ -0,0 +1,49 @@ +package com.ddang.ddang.image.infrastructure.persistence; + +import com.ddang.ddang.configuration.QuerydslConfiguration; +import com.ddang.ddang.image.domain.ProfileImage; +import com.ddang.ddang.image.domain.repository.ProfileImageRepository; +import com.ddang.ddang.image.infrastructure.persistence.fixture.ProfileImageRepositoryImplFixture; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.context.annotation.Import; + +import java.util.Optional; + +import static org.assertj.core.api.Assertions.assertThat; + +@DataJpaTest +@Import(QuerydslConfiguration.class) +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) +@SuppressWarnings("NonAsciiCharacters") +class ProfileImageRepositoryImplTest extends ProfileImageRepositoryImplFixture { + + ProfileImageRepository profileImageRepository; + + @BeforeEach + void setUp(@Autowired final JpaProfileImageRepository jpaProfileImageRepository) { + profileImageRepository = new ProfileImageRepositoryImpl(jpaProfileImageRepository); + } + + @Test + void 프로필을_이미지를_아이디를_통해_조회한다() { + // when + final Optional actual = profileImageRepository.findById(프로필_이미지.getId()); + + // then + assertThat(actual).contains(프로필_이미지); + } + + @Test + void 프로필을_이미지를_저장_이미지를_통해_조회한다() { + // when + final Optional actual = profileImageRepository.findByStoreName(프로필_이미지.getImage().getStoreName()); + + // then + assertThat(actual).contains(프로필_이미지); + } +} diff --git a/backend/ddang/src/test/java/com/ddang/ddang/image/infrastructure/persistence/fixture/AuctionImageRepositoryImplFixture.java b/backend/ddang/src/test/java/com/ddang/ddang/image/infrastructure/persistence/fixture/AuctionImageRepositoryImplFixture.java new file mode 100644 index 000000000..a3a88d949 --- /dev/null +++ b/backend/ddang/src/test/java/com/ddang/ddang/image/infrastructure/persistence/fixture/AuctionImageRepositoryImplFixture.java @@ -0,0 +1,30 @@ +package com.ddang.ddang.image.infrastructure.persistence.fixture; + +import com.ddang.ddang.image.domain.AuctionImage; +import com.ddang.ddang.image.infrastructure.persistence.JpaAuctionImageRepository; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import org.junit.jupiter.api.BeforeEach; +import org.springframework.beans.factory.annotation.Autowired; + +@SuppressWarnings("NonAsciiCharacters") +public class AuctionImageRepositoryImplFixture { + + @PersistenceContext + private EntityManager em; + + @Autowired + private JpaAuctionImageRepository jpaAuctionImageRepository; + + protected AuctionImage 경매_이미지; + + @BeforeEach + void fixtureSetUp() { + 경매_이미지 = new AuctionImage("경매이미지.png", "경매이미지.png"); + + jpaAuctionImageRepository.save(경매_이미지); + + em.flush(); + em.clear(); + } +} diff --git a/backend/ddang/src/test/java/com/ddang/ddang/image/infrastructure/persistence/fixture/ProfileImageRepositoryImplFixture.java b/backend/ddang/src/test/java/com/ddang/ddang/image/infrastructure/persistence/fixture/ProfileImageRepositoryImplFixture.java new file mode 100644 index 000000000..719737f47 --- /dev/null +++ b/backend/ddang/src/test/java/com/ddang/ddang/image/infrastructure/persistence/fixture/ProfileImageRepositoryImplFixture.java @@ -0,0 +1,30 @@ +package com.ddang.ddang.image.infrastructure.persistence.fixture; + +import com.ddang.ddang.image.domain.ProfileImage; +import com.ddang.ddang.image.infrastructure.persistence.JpaProfileImageRepository; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import org.junit.jupiter.api.BeforeEach; +import org.springframework.beans.factory.annotation.Autowired; + +@SuppressWarnings("NonAsciiCharacters") +public class ProfileImageRepositoryImplFixture { + + @PersistenceContext + private EntityManager em; + + @Autowired + private JpaProfileImageRepository profileImageRepository; + + protected ProfileImage 프로필_이미지; + + @BeforeEach + void fixtureSetUp() { + 프로필_이미지 = new ProfileImage("프로필이미지.png", "프로필이미지.png"); + + profileImageRepository.save(프로필_이미지); + + em.flush(); + em.clear(); + } +} diff --git a/backend/ddang/src/test/java/com/ddang/ddang/image/presentation/util/ImageUrlCalculatorTest.java b/backend/ddang/src/test/java/com/ddang/ddang/image/presentation/util/ImageUrlCalculatorTest.java index 741e064d2..491761182 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/image/presentation/util/ImageUrlCalculatorTest.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/image/presentation/util/ImageUrlCalculatorTest.java @@ -4,15 +4,44 @@ import org.junit.jupiter.api.DisplayNameGeneration; import org.junit.jupiter.api.DisplayNameGenerator; import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; import static org.assertj.core.api.Assertions.assertThat; @DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) @SuppressWarnings("NonAsciiCharacters") +@SpringBootTest class ImageUrlCalculatorTest extends ImageUrlCalculatorFixture { @Test - void 프로필_사진의_URL을_계산한다() { + void 프로필_사진의_상대_URL로_절대_경로를_계산한다() { + // when + final String actual = ImageUrlCalculator.calculateBy(프로필_이미지_상대_URL, 프로필_이미지_아이디); + + // then + assertThat(actual).contains(프로필_이미지_전체_URL); + } + + @Test + void 프로필_사진의_아이디가_null인_경우_기본_이미지의_상대_URL로_절대_경로를_계산한다() { + // when + final String actual = ImageUrlCalculator.calculateBy(프로필_이미지_상대_URL, 프로필_이미지_아이디가_null); + + // then + assertThat(actual).contains(프로필_기본_이미지_전체_URL); + } + + @Test + void 경매_대표_이미지의_상대_URL로_절대_경로를_계산한다() { + // when + final String actual = ImageUrlCalculator.calculateBy(경매_이미지_상대_URL, 경매_이미지_아이디); + + // then + assertThat(actual).contains(경매_이미지_전체_URL); + } + + @Test + void 프로필_사진의_절대_URL로_절대_경로를_계산한다() { // when final String actual = ImageUrlCalculator.calculateBy(프로필_이미지_절대_URL, 프로필_이미지_아이디); @@ -21,7 +50,16 @@ class ImageUrlCalculatorTest extends ImageUrlCalculatorFixture { } @Test - void 경매_대표_이미지의_URL을_계산한다() { + void 프로필_사진의_아이디가_null인_경우_기본_이미지의_절대_URL로_절대_경로를_계산한다() { + // when + final String actual = ImageUrlCalculator.calculateBy(프로필_이미지_절대_URL, 프로필_이미지_아이디가_null); + + // then + assertThat(actual).isEqualTo(프로필_기본_이미지_전체_URL); + } + + @Test + void 경매_대표_이미지의_절대_URL로_절대_경로를_계산한다() { // when final String actual = ImageUrlCalculator.calculateBy(경매_이미지_절대_URL, 경매_이미지_아이디); diff --git a/backend/ddang/src/test/java/com/ddang/ddang/image/presentation/util/fixture/ImageUrlCalculatorFixture.java b/backend/ddang/src/test/java/com/ddang/ddang/image/presentation/util/fixture/ImageUrlCalculatorFixture.java index d817dd3a2..abdeaf933 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/image/presentation/util/fixture/ImageUrlCalculatorFixture.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/image/presentation/util/fixture/ImageUrlCalculatorFixture.java @@ -1,12 +1,19 @@ package com.ddang.ddang.image.presentation.util.fixture; +import com.ddang.ddang.image.presentation.util.ImageRelativeUrl; + @SuppressWarnings("NonAsciiCharacters") public class ImageUrlCalculatorFixture { - protected String 프로필_이미지_절대_URL = "http://3-ddang.store/users/images/"; - protected Long 프로필_이미지_아이디 = 1L; + protected ImageRelativeUrl 프로필_이미지_상대_URL = ImageRelativeUrl.USER; + protected ImageRelativeUrl 경매_이미지_상대_URL = ImageRelativeUrl.AUCTION; + + protected String 프로필_이미지_절대_URL = "/users/images/"; + protected Long 프로필_이미지_아이디 = 2L; + protected Long 프로필_이미지_아이디가_null = null; protected String 프로필_이미지_전체_URL = 프로필_이미지_절대_URL + 프로필_이미지_아이디; - protected String 경매_이미지_절대_URL = "http://3-ddang.store/auctions/images/"; + protected String 프로필_기본_이미지_전체_URL = 프로필_이미지_절대_URL + "1"; + protected String 경매_이미지_절대_URL = "/auctions/images/"; protected Long 경매_이미지_아이디 = 1L; protected String 경매_이미지_전체_URL = 경매_이미지_절대_URL + 경매_이미지_아이디; } diff --git a/backend/ddang/src/test/java/com/ddang/ddang/notification/application/NotificationEventListenerTest.java b/backend/ddang/src/test/java/com/ddang/ddang/notification/application/NotificationEventListenerTest.java index 3cce5b25a..661f705a6 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/notification/application/NotificationEventListenerTest.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/notification/application/NotificationEventListenerTest.java @@ -5,7 +5,7 @@ import com.ddang.ddang.bid.infrastructure.persistence.JpaBidRepository; import com.ddang.ddang.chat.application.MessageService; import com.ddang.ddang.chat.application.event.MessageNotificationEvent; -import com.ddang.ddang.chat.infrastructure.persistence.JpaMessageRepository; +import com.ddang.ddang.chat.domain.repository.MessageRepository; import com.ddang.ddang.configuration.IsolateDatabase; import com.ddang.ddang.notification.application.fixture.NotificationEventListenerFixture; import com.ddang.ddang.notification.domain.NotificationStatus; @@ -47,7 +47,7 @@ class NotificationEventListenerTest extends NotificationEventListenerFixture { MessageService messageService; @Autowired - JpaMessageRepository messageRepository; + MessageRepository messageRepository; @Autowired JpaBidRepository bidRepository; @@ -85,7 +85,7 @@ class NotificationEventListenerTest extends NotificationEventListenerFixture { // then SoftAssertions.assertSoftly(softAssertions -> { - softAssertions.assertThat(messageRepository.findById(actualSavedMessageId)).isPresent(); + softAssertions.assertThat(messageRepository.existsById(actualSavedMessageId)).isTrue(); final long actual = events.stream(MessageNotificationEvent.class).count(); softAssertions.assertThat(actual).isEqualTo(1); @@ -128,4 +128,28 @@ class NotificationEventListenerTest extends NotificationEventListenerFixture { // then verify(notificationService).send(any()); } + + @Test + void 이벤트가_호출되면_질문_알림을_전송한다() throws FirebaseMessagingException { + // given + given(notificationService.send(any())).willReturn(NotificationStatus.SUCCESS); + + // when + notificationEventListener.sendQuestionNotification(질문_알림_이벤트); + + // then + verify(notificationService).send(any()); + } + + @Test + void 이벤트가_호출되면_답변_알림을_전송한다() throws FirebaseMessagingException { + // given + given(notificationService.send(any())).willReturn(NotificationStatus.SUCCESS); + + // when + notificationEventListener.sendAnswerNotification(답변_알림_이벤트); + + // then + verify(notificationService).send(any()); + } } diff --git a/backend/ddang/src/test/java/com/ddang/ddang/notification/application/fixture/FcmNotificationServiceFixture.java b/backend/ddang/src/test/java/com/ddang/ddang/notification/application/fixture/FcmNotificationServiceFixture.java index 2fc19c520..5d3a217b8 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/notification/application/fixture/FcmNotificationServiceFixture.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/notification/application/fixture/FcmNotificationServiceFixture.java @@ -3,16 +3,16 @@ import com.ddang.ddang.auction.domain.Auction; import com.ddang.ddang.auction.domain.BidUnit; import com.ddang.ddang.auction.domain.Price; -import com.ddang.ddang.auction.infrastructure.persistence.JpaAuctionRepository; +import com.ddang.ddang.auction.domain.repository.AuctionRepository; import com.ddang.ddang.bid.domain.Bid; import com.ddang.ddang.bid.domain.BidPrice; -import com.ddang.ddang.bid.infrastructure.persistence.JpaBidRepository; +import com.ddang.ddang.bid.domain.repository.BidRepository; import com.ddang.ddang.chat.domain.ChatRoom; import com.ddang.ddang.chat.domain.Message; -import com.ddang.ddang.chat.infrastructure.persistence.JpaChatRoomRepository; -import com.ddang.ddang.chat.infrastructure.persistence.JpaMessageRepository; +import com.ddang.ddang.chat.domain.repository.ChatRoomRepository; +import com.ddang.ddang.chat.domain.repository.MessageRepository; import com.ddang.ddang.device.domain.DeviceToken; -import com.ddang.ddang.device.infrastructure.persistence.JpaDeviceTokenRepository; +import com.ddang.ddang.device.domain.repository.DeviceTokenRepository; import com.ddang.ddang.image.domain.AuctionImage; import com.ddang.ddang.image.domain.ProfileImage; import com.ddang.ddang.image.infrastructure.persistence.JpaAuctionImageRepository; @@ -20,7 +20,7 @@ import com.ddang.ddang.notification.domain.NotificationType; import com.ddang.ddang.user.domain.Reliability; import com.ddang.ddang.user.domain.User; -import com.ddang.ddang.user.infrastructure.persistence.JpaUserRepository; +import com.ddang.ddang.user.domain.repository.UserRepository; import org.junit.jupiter.api.BeforeEach; import org.springframework.beans.factory.annotation.Autowired; @@ -31,30 +31,27 @@ public class FcmNotificationServiceFixture { @Autowired - private JpaUserRepository userRepository; + private UserRepository userRepository; @Autowired - private JpaMessageRepository messageRepository; + private MessageRepository messageRepository; @Autowired - private JpaChatRoomRepository chatRoomRepository; + private ChatRoomRepository chatRoomRepository; @Autowired - private JpaDeviceTokenRepository deviceTokenRepository; + private DeviceTokenRepository deviceTokenRepository; @Autowired - private JpaAuctionRepository auctionRepository; + private AuctionRepository auctionRepository; @Autowired - private JpaBidRepository bidRepository; + private BidRepository bidRepository; @Autowired private JpaAuctionImageRepository auctionImageRepository; protected User 메시지_조회자_겸_발신자; - private User 메시지_수신자; - private User 새로운_입찰자; - private User 기기토큰이_없는_사용자; protected DeviceToken 기기토큰; protected CreateNotificationDto 기기토큰이_없는_사용자의_알림_생성_DTO; protected CreateNotificationDto 알림_생성_DTO; @@ -69,24 +66,24 @@ void setUp() { .reliability(new Reliability(4.7d)) .oauthId("12345") .build(); - 메시지_수신자 = User.builder() - .name("메시지_수신자") - .profileImage(new ProfileImage("upload.png", "store.png")) - .reliability(new Reliability(4.7d)) - .oauthId("12347") - .build(); - 새로운_입찰자 = User.builder() - .name("입찰자1") - .profileImage(new ProfileImage("upload.png", "store.png")) - .reliability(new Reliability(4.7d)) - .oauthId("56789") - .build(); - 기기토큰이_없는_사용자 = User.builder() - .name("기기토큰이 없는 사용자") + final User 메시지_수신자 = User.builder() + .name("메시지_수신자") .profileImage(new ProfileImage("upload.png", "store.png")) .reliability(new Reliability(4.7d)) - .oauthId("12234") + .oauthId("12347") .build(); + final User 새로운_입찰자 = User.builder() + .name("입찰자1") + .profileImage(new ProfileImage("upload.png", "store.png")) + .reliability(new Reliability(4.7d)) + .oauthId("56789") + .build(); + final User 기기토큰이_없는_사용자 = User.builder() + .name("기기토큰이 없는 사용자") + .profileImage(new ProfileImage("upload.png", "store.png")) + .reliability(new Reliability(4.7d)) + .oauthId("12234") + .build(); userRepository.save(메시지_조회자_겸_발신자); userRepository.save(메시지_수신자); diff --git a/backend/ddang/src/test/java/com/ddang/ddang/notification/application/fixture/NotificationEventListenerFixture.java b/backend/ddang/src/test/java/com/ddang/ddang/notification/application/fixture/NotificationEventListenerFixture.java index 72f007c9a..3f7f9c0f2 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/notification/application/fixture/NotificationEventListenerFixture.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/notification/application/fixture/NotificationEventListenerFixture.java @@ -3,27 +3,30 @@ import com.ddang.ddang.auction.domain.Auction; import com.ddang.ddang.auction.domain.BidUnit; import com.ddang.ddang.auction.domain.Price; -import com.ddang.ddang.auction.infrastructure.persistence.JpaAuctionRepository; -import com.ddang.ddang.auction.infrastructure.persistence.dto.AuctionAndImageDto; +import com.ddang.ddang.auction.domain.dto.AuctionAndImageDto; +import com.ddang.ddang.auction.domain.repository.AuctionRepository; import com.ddang.ddang.bid.application.dto.BidDto; import com.ddang.ddang.bid.application.dto.CreateBidDto; import com.ddang.ddang.bid.application.event.BidNotificationEvent; import com.ddang.ddang.bid.domain.Bid; import com.ddang.ddang.bid.domain.BidPrice; -import com.ddang.ddang.bid.infrastructure.persistence.JpaBidRepository; +import com.ddang.ddang.bid.domain.repository.BidRepository; import com.ddang.ddang.chat.application.dto.CreateMessageDto; -import com.ddang.ddang.chat.application.dto.MessageDto; import com.ddang.ddang.chat.application.event.MessageNotificationEvent; import com.ddang.ddang.chat.domain.ChatRoom; import com.ddang.ddang.chat.domain.Message; -import com.ddang.ddang.chat.infrastructure.persistence.JpaChatRoomRepository; +import com.ddang.ddang.chat.domain.repository.ChatRoomRepository; import com.ddang.ddang.chat.infrastructure.persistence.JpaMessageRepository; import com.ddang.ddang.image.domain.AuctionImage; import com.ddang.ddang.image.domain.ProfileImage; import com.ddang.ddang.image.infrastructure.persistence.JpaAuctionImageRepository; +import com.ddang.ddang.qna.application.event.AnswerNotificationEvent; +import com.ddang.ddang.qna.application.event.QuestionNotificationEvent; +import com.ddang.ddang.qna.domain.Answer; +import com.ddang.ddang.qna.domain.Question; import com.ddang.ddang.user.domain.Reliability; import com.ddang.ddang.user.domain.User; -import com.ddang.ddang.user.infrastructure.persistence.JpaUserRepository; +import com.ddang.ddang.user.domain.repository.UserRepository; import org.junit.jupiter.api.BeforeEach; import org.springframework.beans.factory.annotation.Autowired; @@ -34,19 +37,19 @@ public class NotificationEventListenerFixture { @Autowired - private JpaUserRepository userRepository; + private UserRepository userRepository; @Autowired - private JpaChatRoomRepository chatRoomRepository; + private ChatRoomRepository chatRoomRepository; @Autowired - private JpaAuctionRepository auctionRepository; + private AuctionRepository auctionRepository; @Autowired private JpaAuctionImageRepository auctionImageRepository; @Autowired - private JpaBidRepository bidRepository; + private BidRepository bidRepository; @Autowired private JpaMessageRepository messageRepository; @@ -55,11 +58,13 @@ public class NotificationEventListenerFixture { protected CreateBidDto 입찰_생성_DTO; protected MessageNotificationEvent 메시지_알림_이벤트; protected BidNotificationEvent 입찰_알림_이벤트; + protected QuestionNotificationEvent 질문_알림_이벤트; + protected AnswerNotificationEvent 답변_알림_이벤트; protected String 이미지_절대_경로 = "/imageUrl"; @BeforeEach - void setUp() { + void setUpFixture() { final User 발신자_겸_판매자 = User.builder() .name("발신자 겸 판매자") .profileImage(new ProfileImage("upload.png", "store.png")) @@ -78,9 +83,16 @@ void setUp() { .reliability(new Reliability(4.7d)) .oauthId("13579") .build(); + final User 질문자 = User.builder() + .name("질문자") + .profileImage(new ProfileImage("upload.png", "store.png")) + .reliability(new Reliability(4.7d)) + .oauthId("12038") + .build(); userRepository.save(발신자_겸_판매자); userRepository.save(수신자_겸_기존_입찰자); userRepository.save(새로운_입찰자); + userRepository.save(질문자); final Auction 경매 = Auction.builder() .seller(발신자_겸_판매자) @@ -93,33 +105,37 @@ void setUp() { auctionRepository.save(경매); final AuctionImage 경매_이미지 = new AuctionImage("upload.jpg", "store.jpg"); - auctionImageRepository.save(경매_이미지); 경매.addAuctionImages(List.of(경매_이미지)); - final ChatRoom 채팅방 = new ChatRoom(경매, 발신자_겸_판매자); - chatRoomRepository.save(채팅방); - - final Message 메시지 = Message.builder() - .chatRoom(채팅방) - .writer(발신자_겸_판매자) - .receiver(수신자_겸_기존_입찰자) - .contents("메시지 내용") - .build(); - final Message 저장된_메시지 = messageRepository.save(메시지); - - 메시지_생성_DTO = new CreateMessageDto(채팅방.getId(), 발신자_겸_판매자.getId(), 수신자_겸_기존_입찰자.getId(), "메시지 내용"); - final Bid bid = new Bid(경매, 수신자_겸_기존_입찰자, new BidPrice(200)); - bidRepository.save(bid); 경매.updateLastBid(bid); - 입찰_생성_DTO = new CreateBidDto(경매.getId(), 1000, 새로운_입찰자.getId()); + auctionImageRepository.save(경매_이미지); + chatRoomRepository.save(채팅방); + bidRepository.save(bid); - final MessageDto 메시지_DTO = MessageDto.of(저장된_메시지, 채팅방, 발신자_겸_판매자, 수신자_겸_기존_입찰자, 이미지_절대_경로); - 메시지_알림_이벤트 = new MessageNotificationEvent(메시지_DTO); + 메시지_생성_DTO = new CreateMessageDto(채팅방.getId(), 발신자_겸_판매자.getId(), 수신자_겸_기존_입찰자.getId(), "메시지 내용"); + 입찰_생성_DTO = new CreateBidDto(경매.getId(), 1000, 새로운_입찰자.getId()); + final Message 저장된_메시지 = messageRepository.save( + Message.builder() + .chatRoom(채팅방) + .writer(발신자_겸_판매자) + .receiver(수신자_겸_기존_입찰자) + .contents("메시지 내용") + .build() + ); final AuctionAndImageDto auctionAndImageDto = new AuctionAndImageDto(경매, 경매_이미지); final BidDto 입찰_DTO = new BidDto(수신자_겸_기존_입찰자.getId(), auctionAndImageDto, 이미지_절대_경로); + + 메시지_알림_이벤트 = new MessageNotificationEvent(저장된_메시지, 이미지_절대_경로); 입찰_알림_이벤트 = new BidNotificationEvent(입찰_DTO); + + final Question 질문 = new Question(경매, 질문자, "질문 내용"); + final Answer 답변 = new Answer(발신자_겸_판매자, "응답 내용"); + 질문.addAnswer(답변); + + 질문_알림_이벤트 = new QuestionNotificationEvent(질문, 이미지_절대_경로); + 답변_알림_이벤트 = new AnswerNotificationEvent(답변, 이미지_절대_경로); } } diff --git a/backend/ddang/src/test/java/com/ddang/ddang/qna/application/AnswerServiceTest.java b/backend/ddang/src/test/java/com/ddang/ddang/qna/application/AnswerServiceTest.java index f1f7a37d4..1c92a75e8 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/qna/application/AnswerServiceTest.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/qna/application/AnswerServiceTest.java @@ -2,21 +2,26 @@ import com.ddang.ddang.auction.application.exception.UserForbiddenException; import com.ddang.ddang.configuration.IsolateDatabase; +import com.ddang.ddang.qna.application.event.AnswerNotificationEvent; import com.ddang.ddang.qna.application.exception.AlreadyAnsweredException; import com.ddang.ddang.qna.application.exception.AnswerNotFoundException; import com.ddang.ddang.qna.application.exception.InvalidAnswererException; import com.ddang.ddang.qna.application.exception.QuestionNotFoundException; import com.ddang.ddang.qna.application.fixture.AnswerServiceFixture; import com.ddang.ddang.user.application.exception.UserNotFoundException; +import org.assertj.core.api.SoftAssertions; import org.junit.jupiter.api.DisplayNameGeneration; import org.junit.jupiter.api.DisplayNameGenerator; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.event.ApplicationEvents; +import org.springframework.test.context.event.RecordApplicationEvents; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; @IsolateDatabase +@RecordApplicationEvents @DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) @SuppressWarnings("NonAsciiCharacters") class AnswerServiceTest extends AnswerServiceFixture { @@ -24,10 +29,22 @@ class AnswerServiceTest extends AnswerServiceFixture { @Autowired AnswerService answerService; + @Autowired + ApplicationEvents events; + @Test void 답변을_등록한다() { // when - final Long actual = answerService.create(답변_등록_요청_dto); + final Long actual = answerService.create(답변_등록_요청_dto, 이미지_절대_경로); + + // then + assertThat(actual).isPositive(); + } + + @Test + void 답변을_삭제한_질문에_다시_답변을_달_수_있다() { + // when + final Long actual = answerService.create(답변이_삭제된_질문에_답변_등록_요청_dto, 이미지_절대_경로); // then assertThat(actual).isPositive(); @@ -36,7 +53,7 @@ class AnswerServiceTest extends AnswerServiceFixture { @Test void 존재하지_않는_사용자가_질문에_답하는_경우_예외가_발생한다() { // when & then - assertThatThrownBy(() -> answerService.create(존재하지_않는_사용자의_답변_등록_요청_dto)) + assertThatThrownBy(() -> answerService.create(존재하지_않는_사용자의_답변_등록_요청_dto, 이미지_절대_경로)) .isInstanceOf(UserNotFoundException.class) .hasMessage("해당 사용자를 찾을 수 없습니다."); } @@ -44,7 +61,7 @@ class AnswerServiceTest extends AnswerServiceFixture { @Test void 존재하지_않는_질문에_답하는_경우_예외가_발생한다() { // when & then - assertThatThrownBy(() -> answerService.create(존재하지_않는_질문에_답변_등록_요청_dto)) + assertThatThrownBy(() -> answerService.create(존재하지_않는_질문에_답변_등록_요청_dto, 이미지_절대_경로)) .isInstanceOf(QuestionNotFoundException.class) .hasMessage("해당 질문을 찾을 수 없습니다."); } @@ -52,7 +69,7 @@ class AnswerServiceTest extends AnswerServiceFixture { @Test void 판매자가_아닌_다른_사용자가_질문에_답하는_경우_예외가_발생한다() { // when & then - assertThatThrownBy(() -> answerService.create(판매자가_아닌_사용자가_질문에_답변_등록_요청_dto)) + assertThatThrownBy(() -> answerService.create(판매자가_아닌_사용자가_질문에_답변_등록_요청_dto, 이미지_절대_경로)) .isInstanceOf(InvalidAnswererException.class) .hasMessage("판매자만 답변할 수 있습니다."); } @@ -60,7 +77,7 @@ class AnswerServiceTest extends AnswerServiceFixture { @Test void 이미_답변한_질문에_답하는_경우_예외가_발생한다() { // when & then - assertThatThrownBy(() -> answerService.create(이미_답변한_질문에_답변_등록_요청_dto)) + assertThatThrownBy(() -> answerService.create(이미_답변한_질문에_답변_등록_요청_dto, 이미지_절대_경로)) .isInstanceOf(AlreadyAnsweredException.class) .hasMessage("이미 답변한 질문입니다."); } @@ -71,7 +88,11 @@ class AnswerServiceTest extends AnswerServiceFixture { answerService.deleteById(답변.getId(), 판매자.getId()); // then - assertThat(답변.isDeleted()).isTrue(); + SoftAssertions.assertSoftly(softAssertions -> { + softAssertions.assertThat(답변.isDeleted()).isTrue(); + softAssertions.assertThat(답변.getQuestion()).isNull(); + softAssertions.assertThat(답변한_질문.getAnswer()).isNull(); + }); } @Test @@ -97,4 +118,14 @@ class AnswerServiceTest extends AnswerServiceFixture { .isInstanceOf(UserForbiddenException.class) .hasMessage("삭제할 권한이 없습니다."); } + + @Test + void 질문에_대한_답변이_생성되면_질문자에게_알림을_보낸다() { + // when + answerService.create(답변_등록_요청_dto, 이미지_절대_경로); + final long actual = events.stream(AnswerNotificationEvent.class).count(); + + // then + assertThat(actual).isEqualTo(1); + } } diff --git a/backend/ddang/src/test/java/com/ddang/ddang/qna/application/QuestionServiceTest.java b/backend/ddang/src/test/java/com/ddang/ddang/qna/application/QuestionServiceTest.java index c994b7f55..3a629fa94 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/qna/application/QuestionServiceTest.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/qna/application/QuestionServiceTest.java @@ -5,6 +5,7 @@ import com.ddang.ddang.configuration.IsolateDatabase; import com.ddang.ddang.qna.application.dto.ReadQnaDto; import com.ddang.ddang.qna.application.dto.ReadQnasDto; +import com.ddang.ddang.qna.application.event.QuestionNotificationEvent; import com.ddang.ddang.qna.application.exception.InvalidAuctionToAskQuestionException; import com.ddang.ddang.qna.application.exception.InvalidQuestionerException; import com.ddang.ddang.qna.application.exception.QuestionNotFoundException; @@ -15,6 +16,8 @@ import org.junit.jupiter.api.DisplayNameGenerator; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.event.ApplicationEvents; +import org.springframework.test.context.event.RecordApplicationEvents; import java.util.List; @@ -22,6 +25,7 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; @IsolateDatabase +@RecordApplicationEvents @DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) @SuppressWarnings("NonAsciiCharacters") class QuestionServiceTest extends QuestionServiceFixture { @@ -29,10 +33,13 @@ class QuestionServiceTest extends QuestionServiceFixture { @Autowired QuestionService questionService; + @Autowired + ApplicationEvents events; + @Test void 질문을_등록한다() { // when - final Long actual = questionService.create(경매_질문_등록_요청_dto); + final Long actual = questionService.create(경매_질문_등록_요청_dto, 이미지_절대_경로); // then assertThat(actual).isPositive(); @@ -41,7 +48,7 @@ class QuestionServiceTest extends QuestionServiceFixture { @Test void 존재하지_않는_사용자가_경매에_질문하는_경우_예외가_발생한다() { // when & then - assertThatThrownBy(() -> questionService.create(존재하지_않는_사용자가_경매_질문_등록_요청_dto)) + assertThatThrownBy(() -> questionService.create(존재하지_않는_사용자가_경매_질문_등록_요청_dto, 이미지_절대_경로)) .isInstanceOf(UserNotFoundException.class) .hasMessage("해당 사용자를 찾을 수 없습니다."); } @@ -49,7 +56,7 @@ class QuestionServiceTest extends QuestionServiceFixture { @Test void 존재하지_않는_경매에_질문하는_경우_예외가_발생한다() { // when & then - assertThatThrownBy(() -> questionService.create(존재하지_않는_경매_질문_등록_요청_dto)) + assertThatThrownBy(() -> questionService.create(존재하지_않는_경매_질문_등록_요청_dto, 이미지_절대_경로)) .isInstanceOf(AuctionNotFoundException.class) .hasMessage("해당 경매를 찾을 수 없습니다."); } @@ -57,7 +64,7 @@ class QuestionServiceTest extends QuestionServiceFixture { @Test void 이미_종료된_경매에_질문하는_경우_예외가_발생한다() { // when & then - assertThatThrownBy(() -> questionService.create(종료된_경매_질문_등록_요청_dto)) + assertThatThrownBy(() -> questionService.create(종료된_경매_질문_등록_요청_dto, 이미지_절대_경로)) .isInstanceOf(InvalidAuctionToAskQuestionException.class) .hasMessage("이미 종료된 경매입니다."); } @@ -65,7 +72,7 @@ class QuestionServiceTest extends QuestionServiceFixture { @Test void 삭제된_경매에_질문하는_경우_예외가_발생한다() { // when & then - assertThatThrownBy(() -> questionService.create(삭제된_경매_질문_등록_요청_dto)) + assertThatThrownBy(() -> questionService.create(삭제된_경매_질문_등록_요청_dto, 이미지_절대_경로)) .isInstanceOf(AuctionNotFoundException.class) .hasMessage("해당 경매를 찾을 수 없습니다."); } @@ -73,7 +80,7 @@ class QuestionServiceTest extends QuestionServiceFixture { @Test void 판매자가_본인_경매에_질문하는_경우_예외가_발생한다() { // when & then - assertThatThrownBy(() -> questionService.create(판매자가_본인_경매_질문_등록_요청_dto)) + assertThatThrownBy(() -> questionService.create(판매자가_본인_경매_질문_등록_요청_dto, 이미지_절대_경로)) .isInstanceOf(InvalidQuestionerException.class) .hasMessage("경매 등록자는 질문할 수 없습니다."); } @@ -81,7 +88,7 @@ class QuestionServiceTest extends QuestionServiceFixture { @Test void 경매_아이디를_통해_질문과_답변을_모두_조회한다() { // when - final ReadQnasDto actual = questionService.readAllByAuctionId(질문_3개_답변_2개가_존재하는_경매_아이디); + final ReadQnasDto actual = questionService.readAllByAuctionId(질문_3개_답변_2개가_존재하는_경매_아이디, 두번째_질문을_작성한_사용자.getId()); // then SoftAssertions.assertSoftly(softAssertions -> { @@ -90,14 +97,17 @@ class QuestionServiceTest extends QuestionServiceFixture { final ReadQnaDto 첫번째_질문 = questionAndAnswerDtos.get(0); softAssertions.assertThat(첫번째_질문.readQuestionDto()).isEqualTo(질문_정보_dto1); + softAssertions.assertThat(첫번째_질문.readQuestionDto().isQuestioner()).isFalse(); softAssertions.assertThat(첫번째_질문.readAnswerDto()).isEqualTo(답변_정보_dto1); final ReadQnaDto 두번째_질문 = questionAndAnswerDtos.get(1); softAssertions.assertThat(두번째_질문.readQuestionDto()).isEqualTo(질문_정보_dto2); + softAssertions.assertThat(두번째_질문.readQuestionDto().isQuestioner()).isTrue(); softAssertions.assertThat(두번째_질문.readAnswerDto()).isEqualTo(답변_정보_dto2); final ReadQnaDto 세번째_질문 = questionAndAnswerDtos.get(2); softAssertions.assertThat(세번째_질문.readQuestionDto()).isEqualTo(질문_정보_dto3); + softAssertions.assertThat(세번째_질문.readQuestionDto().isQuestioner()).isFalse(); softAssertions.assertThat(세번째_질문.readAnswerDto()).isNull(); }); } @@ -105,7 +115,7 @@ class QuestionServiceTest extends QuestionServiceFixture { @Test void 존재하지_않는_경매_아이디를_통해_질문과_답변을_모두_조회할시_예외가_발생한다() { // when & then - assertThatThrownBy(() -> questionService.readAllByAuctionId(존재하지_않는_경매_아이디)) + assertThatThrownBy(() -> questionService.readAllByAuctionId(존재하지_않는_경매_아이디, 질문하지_않은_사용자.getId())) .isInstanceOf(AuctionNotFoundException.class) .hasMessage("해당 경매를 찾을 수 없습니다."); } @@ -142,4 +152,14 @@ class QuestionServiceTest extends QuestionServiceFixture { .isInstanceOf(UserForbiddenException.class) .hasMessage("삭제할 권한이 없습니다."); } + + @Test + void 질문이_생성되면_판매자에게_알림을_보낸다() { + // when + questionService.create(경매_질문_등록_요청_dto, 이미지_절대_경로); + final long actual = events.stream(QuestionNotificationEvent.class).count(); + + // then + assertThat(actual).isEqualTo(1); + } } diff --git a/backend/ddang/src/test/java/com/ddang/ddang/qna/application/fixture/AnswerServiceFixture.java b/backend/ddang/src/test/java/com/ddang/ddang/qna/application/fixture/AnswerServiceFixture.java index 28afd100c..f950e072d 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/qna/application/fixture/AnswerServiceFixture.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/qna/application/fixture/AnswerServiceFixture.java @@ -3,39 +3,41 @@ import com.ddang.ddang.auction.domain.Auction; import com.ddang.ddang.auction.domain.BidUnit; import com.ddang.ddang.auction.domain.Price; -import com.ddang.ddang.auction.infrastructure.persistence.JpaAuctionRepository; +import com.ddang.ddang.auction.domain.repository.AuctionRepository; import com.ddang.ddang.image.domain.ProfileImage; import com.ddang.ddang.qna.application.dto.CreateAnswerDto; import com.ddang.ddang.qna.domain.Answer; import com.ddang.ddang.qna.domain.Question; -import com.ddang.ddang.qna.infrastructure.JpaAnswerRepository; -import com.ddang.ddang.qna.infrastructure.JpaQuestionRepository; +import com.ddang.ddang.qna.domain.repository.AnswerRepository; +import com.ddang.ddang.qna.domain.repository.QuestionRepository; import com.ddang.ddang.user.domain.Reliability; import com.ddang.ddang.user.domain.User; -import com.ddang.ddang.user.infrastructure.persistence.JpaUserRepository; +import com.ddang.ddang.user.domain.repository.UserRepository; import org.junit.jupiter.api.BeforeEach; import org.springframework.beans.factory.annotation.Autowired; import java.time.LocalDateTime; -import java.util.List; @SuppressWarnings("NonAsciiCharacters") public class AnswerServiceFixture { @Autowired - private JpaAuctionRepository auctionRepository; + private AuctionRepository auctionRepository; @Autowired - private JpaUserRepository userRepository; + private UserRepository userRepository; @Autowired - private JpaQuestionRepository questionRepository; + private QuestionRepository questionRepository; @Autowired - private JpaAnswerRepository answerRepository; + private AnswerRepository answerRepository; protected Long 존재하지_않는_답변_아이디 = -999L; protected Long 존재하지_않는_사용자_아이디 = -999L; + protected String 이미지_절대_경로 = "/imageUrl"; + + protected Question 답변한_질문; protected Answer 답변; protected User 판매자; protected User 판매자가_아닌_사용자; @@ -45,6 +47,7 @@ public class AnswerServiceFixture { protected CreateAnswerDto 존재하지_않는_질문에_답변_등록_요청_dto; protected CreateAnswerDto 판매자가_아닌_사용자가_질문에_답변_등록_요청_dto; protected CreateAnswerDto 이미_답변한_질문에_답변_등록_요청_dto; + protected CreateAnswerDto 답변이_삭제된_질문에_답변_등록_요청_dto; @BeforeEach void setUp() { @@ -76,19 +79,33 @@ void setUp() { .oauthId("12347") .build(); final Question 질문 = new Question(경매, 질문자, "궁금한 점이 있습니다."); - final Question 답변한_질문 = new Question(경매, 질문자, "궁금한 점이 있습니다."); - 답변 = new Answer("답변드립니다."); + final Question 삭제된_답변이_있는_질문 = new Question(경매, 질문자, "궁금한 점이 있습니다."); + 답변한_질문 = new Question(경매, 질문자, "궁금한 점이 있습니다."); + 답변 = new Answer(판매자, "답변드립니다."); 답변한_질문.addAnswer(답변); - userRepository.saveAll(List.of(판매자, 질문자, 판매자가_아닌_사용자)); + final Answer 삭제된_답변 = new Answer(판매자, "삭제할 답변입니다"); + + userRepository.save(판매자); + userRepository.save(질문자); + userRepository.save(판매자가_아닌_사용자); + auctionRepository.save(경매); - questionRepository.saveAll(List.of(질문, 답변한_질문)); + + questionRepository.save(질문); + questionRepository.save(답변한_질문); + questionRepository.save(삭제된_답변이_있는_질문); + answerRepository.save(답변); + answerRepository.save(삭제된_답변); + + 삭제된_답변.delete(); 답변_등록_요청_dto = new CreateAnswerDto(질문.getId(), "답변 드립니다.", 판매자.getId()); 존재하지_않는_사용자의_답변_등록_요청_dto = new CreateAnswerDto(질문.getId(), "답변 드립니다.", -999L); 존재하지_않는_질문에_답변_등록_요청_dto = new CreateAnswerDto(-999L, "답변 드립니다.", 판매자.getId()); 판매자가_아닌_사용자가_질문에_답변_등록_요청_dto = new CreateAnswerDto(질문.getId(), "답변 드립니다.", 질문자.getId()); 이미_답변한_질문에_답변_등록_요청_dto = new CreateAnswerDto(답변한_질문.getId(), "답변 드립니다.", 판매자.getId()); + 답변이_삭제된_질문에_답변_등록_요청_dto = new CreateAnswerDto(삭제된_답변이_있는_질문.getId(), "답변드립니다", 판매자.getId()); } } diff --git a/backend/ddang/src/test/java/com/ddang/ddang/qna/application/fixture/QuestionServiceFixture.java b/backend/ddang/src/test/java/com/ddang/ddang/qna/application/fixture/QuestionServiceFixture.java index 215c975fa..b9577e3a8 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/qna/application/fixture/QuestionServiceFixture.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/qna/application/fixture/QuestionServiceFixture.java @@ -3,7 +3,9 @@ import com.ddang.ddang.auction.domain.Auction; import com.ddang.ddang.auction.domain.BidUnit; import com.ddang.ddang.auction.domain.Price; -import com.ddang.ddang.auction.infrastructure.persistence.JpaAuctionRepository; +import com.ddang.ddang.auction.domain.repository.AuctionRepository; +import com.ddang.ddang.category.domain.Category; +import com.ddang.ddang.category.infrastructure.persistence.JpaCategoryRepository; import com.ddang.ddang.image.domain.ProfileImage; import com.ddang.ddang.qna.application.dto.CreateQuestionDto; import com.ddang.ddang.qna.application.dto.ReadAnswerDto; @@ -11,31 +13,38 @@ import com.ddang.ddang.qna.application.dto.ReadUserInQnaDto; import com.ddang.ddang.qna.domain.Answer; import com.ddang.ddang.qna.domain.Question; -import com.ddang.ddang.qna.infrastructure.JpaAnswerRepository; -import com.ddang.ddang.qna.infrastructure.JpaQuestionRepository; +import com.ddang.ddang.qna.domain.repository.AnswerRepository; +import com.ddang.ddang.qna.domain.repository.QuestionRepository; +import com.ddang.ddang.region.domain.Region; +import com.ddang.ddang.region.domain.repository.RegionRepository; import com.ddang.ddang.user.domain.Reliability; import com.ddang.ddang.user.domain.User; -import com.ddang.ddang.user.infrastructure.persistence.JpaUserRepository; +import com.ddang.ddang.user.domain.repository.UserRepository; import org.junit.jupiter.api.BeforeEach; import org.springframework.beans.factory.annotation.Autowired; import java.time.LocalDateTime; -import java.util.List; @SuppressWarnings("NonAsciiCharacters") public class QuestionServiceFixture { @Autowired - private JpaUserRepository userRepository; + private UserRepository userRepository; @Autowired - private JpaAuctionRepository auctionRepository; + private AuctionRepository auctionRepository; @Autowired - private JpaQuestionRepository questionRepository; + private QuestionRepository questionRepository; @Autowired - private JpaAnswerRepository answerRepository; + private AnswerRepository answerRepository; + + @Autowired + private RegionRepository regionRepository; + + @Autowired + private JpaCategoryRepository categoryRepository; protected Long 질문_3개_답변_2개가_존재하는_경매_아이디; protected Long 존재하지_않는_경매_아이디 = -999L; @@ -44,6 +53,7 @@ public class QuestionServiceFixture { protected Question 질문; protected User 질문자; protected User 질문하지_않은_사용자; + protected User 두번째_질문을_작성한_사용자; protected CreateQuestionDto 경매_질문_등록_요청_dto; protected CreateQuestionDto 존재하지_않는_사용자가_경매_질문_등록_요청_dto; protected CreateQuestionDto 존재하지_않는_경매_질문_등록_요청_dto; @@ -56,8 +66,26 @@ public class QuestionServiceFixture { protected ReadAnswerDto 답변_정보_dto1; protected ReadAnswerDto 답변_정보_dto2; + protected String 이미지_절대_경로 = "/imageUrl"; + @BeforeEach void setUp() { + final Region 서울특별시 = new Region("서울특별시"); + final Region 강남구 = new Region("강남구"); + final Region 역삼동 = new Region("역삼동"); + + 서울특별시.addSecondRegion(강남구); + 강남구.addThirdRegion(역삼동); + + regionRepository.save(서울특별시); + + final Category 가구_카테고리 = new Category("가구"); + final Category 가구_서브_의자_카테고리 = new Category("의자"); + + 가구_카테고리.addSubCategory(가구_서브_의자_카테고리); + + categoryRepository.save(가구_카테고리); + final ProfileImage 프로필_이미지 = new ProfileImage("프로필.jpg", "프로필.jpg"); final User 판매자 = User.builder() .name("판매자") @@ -72,6 +100,7 @@ void setUp() { .bidUnit(new BidUnit(1_000)) .startPrice(new Price(1_000)) .closingTime(LocalDateTime.now().plusDays(7)) + .subCategory(가구_서브_의자_카테고리) .build(); final Auction 질문과_답변이_존재하는_경매 = Auction.builder() .seller(판매자) @@ -80,6 +109,7 @@ void setUp() { .bidUnit(new BidUnit(1_000)) .startPrice(new Price(1_000)) .closingTime(LocalDateTime.now().plusDays(7)) + .subCategory(가구_서브_의자_카테고리) .build(); final Auction 종료된_경매 = Auction.builder() .seller(판매자) @@ -88,6 +118,7 @@ void setUp() { .bidUnit(new BidUnit(1_000)) .startPrice(new Price(1_000)) .closingTime(LocalDateTime.now().minusDays(7)) + .subCategory(가구_서브_의자_카테고리) .build(); final Auction 삭제된_경매 = Auction.builder() .seller(판매자) @@ -96,6 +127,7 @@ void setUp() { .bidUnit(new BidUnit(1_000)) .startPrice(new Price(1_000)) .closingTime(LocalDateTime.now().plusDays(7)) + .subCategory(가구_서브_의자_카테고리) .build(); 삭제된_경매.delete(); 질문자 = User.builder() @@ -110,18 +142,34 @@ void setUp() { .reliability(new Reliability(4.7d)) .oauthId("12346") .build(); + 두번째_질문을_작성한_사용자 = User.builder() + .name("두번째 질문자") + .profileImage(프로필_이미지) + .reliability(new Reliability(4.7d)) + .oauthId("12347") + .build(); 질문 = new Question(질문과_답변이_존재하는_경매, 질문자, "질문1"); - final Question 질문2 = new Question(질문과_답변이_존재하는_경매, 질문자, "질문2"); + final Question 질문2 = new Question(질문과_답변이_존재하는_경매, 두번째_질문을_작성한_사용자, "질문2"); final Question 질문3 = new Question(질문과_답변이_존재하는_경매, 질문자, "질문3"); - final Answer 답변1 = new Answer("답변1"); - final Answer 답변2 = new Answer("답변2"); + final Answer 답변1 = new Answer(판매자, "답변1"); + final Answer 답변2 = new Answer(판매자, "답변2"); 질문.addAnswer(답변1); 질문2.addAnswer(답변2); - userRepository.saveAll(List.of(판매자, 질문자, 질문하지_않은_사용자)); - auctionRepository.saveAll(List.of(경매, 질문과_답변이_존재하는_경매, 종료된_경매, 삭제된_경매)); - questionRepository.saveAll(List.of(질문, 질문2, 질문3)); - answerRepository.saveAll(List.of(답변1, 답변2)); + userRepository.save(판매자); + userRepository.save(질문자); + userRepository.save(질문하지_않은_사용자); + userRepository.save(두번째_질문을_작성한_사용자); + + auctionRepository.save(경매); + auctionRepository.save(질문과_답변이_존재하는_경매); + auctionRepository.save(종료된_경매); + auctionRepository.save(삭제된_경매); + questionRepository.save(질문); + questionRepository.save(질문2); + questionRepository.save(질문3); + answerRepository.save(답변1); + answerRepository.save(답변2); 질문_3개_답변_2개가_존재하는_경매_아이디 = 질문과_답변이_존재하는_경매.getId(); @@ -134,10 +182,11 @@ void setUp() { final ReadUserInQnaDto 판매자_정보_dto = ReadUserInQnaDto.from(판매자); final ReadUserInQnaDto 질문자_정보_dto = ReadUserInQnaDto.from(질문자); - 질문_정보_dto1 = new ReadQuestionDto(질문.getId(), 질문자_정보_dto, 질문.getContent(), 질문.getCreatedTime()); - 질문_정보_dto2 = new ReadQuestionDto(질문2.getId(), 질문자_정보_dto, 질문2.getContent(), 질문2.getCreatedTime()); - 질문_정보_dto3 = new ReadQuestionDto(질문3.getId(), 질문자_정보_dto, 질문3.getContent(), 질문3.getCreatedTime()); - 답변_정보_dto1 = new ReadAnswerDto(답변1.getId(), 판매자_정보_dto, 답변1.getContent(), 답변1.getCreatedTime()); - 답변_정보_dto2 = new ReadAnswerDto(답변2.getId(), 판매자_정보_dto, 답변2.getContent(), 답변2.getCreatedTime()); + final ReadUserInQnaDto 두번째_질문자_정보_dto = ReadUserInQnaDto.from(두번째_질문을_작성한_사용자); + 질문_정보_dto1 = new ReadQuestionDto(질문.getId(), 질문자_정보_dto, 질문.getContent(), 질문.getCreatedTime(), false, false); + 질문_정보_dto2 = new ReadQuestionDto(질문2.getId(), 두번째_질문자_정보_dto, 질문2.getContent(), 질문2.getCreatedTime(), false, true); + 질문_정보_dto3 = new ReadQuestionDto(질문3.getId(), 질문자_정보_dto, 질문3.getContent(), 질문3.getCreatedTime(), false, false); + 답변_정보_dto1 = new ReadAnswerDto(답변1.getId(), 판매자_정보_dto, 답변1.getContent(), 답변1.getCreatedTime(), false); + 답변_정보_dto2 = new ReadAnswerDto(답변2.getId(), 판매자_정보_dto, 답변2.getContent(), 답변2.getCreatedTime(), false); } } diff --git a/backend/ddang/src/test/java/com/ddang/ddang/qna/domain/AnswerTest.java b/backend/ddang/src/test/java/com/ddang/ddang/qna/domain/AnswerTest.java index a6d4228e4..5cb8a6c6c 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/qna/domain/AnswerTest.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/qna/domain/AnswerTest.java @@ -2,6 +2,7 @@ import com.ddang.ddang.qna.domain.fixture.AnswerFixture; import com.ddang.ddang.user.domain.User; +import org.assertj.core.api.SoftAssertions; import org.junit.jupiter.api.DisplayNameGeneration; import org.junit.jupiter.api.DisplayNameGenerator; import org.junit.jupiter.api.Test; @@ -15,7 +16,7 @@ class AnswerTest extends AnswerFixture { @Test void 답변과_질문의_연관관계를_세팅한다() { // given - final Answer answer = new Answer("답변드립니다."); + final Answer answer = new Answer(판매자, "답변드립니다."); // when answer.initQuestion(질문); @@ -68,4 +69,17 @@ class AnswerTest extends AnswerFixture { // then assertThat(actual).isEqualTo(판매자); } + + @Test + void 답변_삭제시_삭제_상태를_참으로_변경하고_질문과의_연관관계를_끊는다() { + // when + 답변.delete(); + + // then + SoftAssertions.assertSoftly(softAssertions -> { + softAssertions.assertThat(답변.isDeleted()).isTrue(); + softAssertions.assertThat(답변.getQuestion()).isNull(); + softAssertions.assertThat(답변이_있는_질문.getAnswer()).isNull(); + }); + } } diff --git a/backend/ddang/src/test/java/com/ddang/ddang/qna/domain/fixture/AnswerFixture.java b/backend/ddang/src/test/java/com/ddang/ddang/qna/domain/fixture/AnswerFixture.java index 0099a5a5f..a465f0039 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/qna/domain/fixture/AnswerFixture.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/qna/domain/fixture/AnswerFixture.java @@ -36,9 +36,10 @@ public class AnswerFixture { protected User 답변_작성자가_아닌_사용자 = 질문_작성자; protected Question 질문 = new Question(경매, 질문_작성자, "궁금한 점이 있어요."); - private Question 답변이_있는_질문 = new Question(경매, 질문_작성자, "궁금한 점이 있어요."); - protected Answer 답변 = new Answer("답변드립니다."); - protected Answer 삭제된_답변 = new Answer("답변드립니다."); + protected Question 답변이_있는_질문 = new Question(경매, 질문_작성자, "궁금한 점이 있어요."); + private Question 삭제된_답변이_있는_질문 = new Question(경매, 질문_작성자, "궁금한 점이 있어요."); + protected Answer 답변 = new Answer(판매자, "답변드립니다."); + protected Answer 삭제된_답변 = new Answer(판매자, "답변드립니다."); @BeforeEach void setUp() { @@ -49,6 +50,7 @@ void setUp() { ReflectionTestUtils.setField(삭제된_답변, "id", 1L); 답변이_있는_질문.addAnswer(답변); + 삭제된_답변이_있는_질문.addAnswer(삭제된_답변); 삭제된_답변.delete(); } } diff --git a/backend/ddang/src/test/java/com/ddang/ddang/qna/domain/fixture/QuestionFixture.java b/backend/ddang/src/test/java/com/ddang/ddang/qna/domain/fixture/QuestionFixture.java index deee8b3eb..cfbc61889 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/qna/domain/fixture/QuestionFixture.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/qna/domain/fixture/QuestionFixture.java @@ -42,7 +42,7 @@ public class QuestionFixture { protected Question 질문 = new Question(경매, 질문_작성자, 질문_내용); protected Question 삭제된_질문 = new Question(경매, 질문_작성자, 질문_내용); - protected Answer 답변 = new Answer("답변드립니다."); + protected Answer 답변 = new Answer(판매자, "답변드립니다."); @BeforeEach void setUp() { diff --git a/backend/ddang/src/test/java/com/ddang/ddang/qna/infrastructure/AnswerRepositoryImplTest.java b/backend/ddang/src/test/java/com/ddang/ddang/qna/infrastructure/AnswerRepositoryImplTest.java new file mode 100644 index 000000000..c44cea576 --- /dev/null +++ b/backend/ddang/src/test/java/com/ddang/ddang/qna/infrastructure/AnswerRepositoryImplTest.java @@ -0,0 +1,81 @@ +package com.ddang.ddang.qna.infrastructure; + +import com.ddang.ddang.configuration.JpaConfiguration; +import com.ddang.ddang.configuration.QuerydslConfiguration; +import com.ddang.ddang.qna.domain.Answer; +import com.ddang.ddang.qna.domain.repository.AnswerRepository; +import com.ddang.ddang.qna.infrastructure.fixture.AnswerRepositoryImplFixture; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.context.annotation.Import; + +import java.util.Optional; + +import static org.assertj.core.api.Assertions.assertThat; + +@DataJpaTest +@Import({JpaConfiguration.class, QuerydslConfiguration.class}) +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) +@SuppressWarnings("NonAsciiCharacters") +class AnswerRepositoryImplTest extends AnswerRepositoryImplFixture { + + AnswerRepository answerRepository; + + @BeforeEach + void setUp(@Autowired final JpaAnswerRepository jpaAnswerRepository) { + answerRepository = new AnswerRepositoryImpl(jpaAnswerRepository); + } + + @Test + void 답변을_저장한다() { + // given + final Answer answer = new Answer(판매자, 답변_내용); + 질문.addAnswer(answer); + + // when + final Answer actual = answerRepository.save(answer); + + // then + assertThat(actual.getId()).isPositive(); + } + + @Test + void 이미_질문에_대한_답변이_존재한다면_참을_반환한다() { + // when + final boolean actual = answerRepository.existsByQuestionId(답변이_존재하는_질문.getId()); + + // then + assertThat(actual).isTrue(); + } + + @Test + void 이미_질문에_대한_답변이_존재하지_않는다면_거짓을_반환한다() { + // when + final boolean actual = answerRepository.existsByQuestionId(답변이_존재하지_않는_질문.getId()); + + // then + assertThat(actual).isFalse(); + } + + @Test + void 삭제된_답변은_조회되지_않는다() { + // when + final Optional actual = answerRepository.findById(삭제된_답변.getId()); + + // then + assertThat(actual).isEmpty(); + } + + @Test + void 삭제되지_않은_답변은_조회된다() { + // when + final Optional actual = answerRepository.findById(답변.getId()); + + // then + assertThat(actual).contains(답변); + } +} diff --git a/backend/ddang/src/test/java/com/ddang/ddang/qna/infrastructure/JpaAnswerRepositoryTest.java b/backend/ddang/src/test/java/com/ddang/ddang/qna/infrastructure/JpaAnswerRepositoryTest.java index bd3a53533..6b77da6c9 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/qna/infrastructure/JpaAnswerRepositoryTest.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/qna/infrastructure/JpaAnswerRepositoryTest.java @@ -27,7 +27,7 @@ class JpaAnswerRepositoryTest extends JpaAnswerRepositoryFixture { @Test void 답변을_저장한다() { // given - final Answer answer = new Answer(답변_내용); + final Answer answer = new Answer(판매자, 답변_내용); 질문.addAnswer(answer); // when diff --git a/backend/ddang/src/test/java/com/ddang/ddang/qna/infrastructure/JpaQuestionRepositoryTest.java b/backend/ddang/src/test/java/com/ddang/ddang/qna/infrastructure/JpaQuestionRepositoryTest.java index f3ff3c557..ee99840a2 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/qna/infrastructure/JpaQuestionRepositoryTest.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/qna/infrastructure/JpaQuestionRepositoryTest.java @@ -72,4 +72,19 @@ class JpaQuestionRepositoryTest extends JpaQuestionRepositoryFixture { softAssertions.assertThat(actual.get(2).getAnswer()).isNull(); }); } + + @Test + void 경매_아이디를_통해_질문과_답변_조회시_질문이_삭제된_경우_답변이_있더라도_함꼐_조회되지_않는다() { + // when + final List actual = questionRepository.findAllByAuctionId(질문이_3개_답변이_2개중_첫번째_질문이_삭제된_경매.getId()); + + // then + SoftAssertions.assertSoftly(softAssertions -> { + softAssertions.assertThat(actual).hasSize(2); + softAssertions.assertThat(actual.get(0)).isEqualTo(삭제되지_않은_질문2); + softAssertions.assertThat(actual.get(0).getAnswer()).isEqualTo(삭제되지_않은_질문의_답변2); + softAssertions.assertThat(actual.get(1)).isEqualTo(답변이_없는_질문3); + softAssertions.assertThat(actual.get(1).getAnswer()).isNull(); + }); + } } diff --git a/backend/ddang/src/test/java/com/ddang/ddang/qna/infrastructure/QuestionRepositoryImplTest.java b/backend/ddang/src/test/java/com/ddang/ddang/qna/infrastructure/QuestionRepositoryImplTest.java new file mode 100644 index 000000000..f3522af97 --- /dev/null +++ b/backend/ddang/src/test/java/com/ddang/ddang/qna/infrastructure/QuestionRepositoryImplTest.java @@ -0,0 +1,81 @@ +package com.ddang.ddang.qna.infrastructure; + +import com.ddang.ddang.configuration.JpaConfiguration; +import com.ddang.ddang.configuration.QuerydslConfiguration; +import com.ddang.ddang.qna.domain.Question; +import com.ddang.ddang.qna.domain.repository.QuestionRepository; +import com.ddang.ddang.qna.infrastructure.fixture.QuestionRepositoryImplFixture; +import org.assertj.core.api.SoftAssertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.context.annotation.Import; + +import java.util.List; +import java.util.Optional; + +import static org.assertj.core.api.Assertions.assertThat; + +@DataJpaTest +@Import({JpaConfiguration.class, QuerydslConfiguration.class}) +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) +@SuppressWarnings("NonAsciiCharacters") +class QuestionRepositoryImplTest extends QuestionRepositoryImplFixture { + + QuestionRepository questionRepository; + + @BeforeEach + void setUp(@Autowired final JpaQuestionRepository jpaQuestionRepository) { + questionRepository = new QuestionRepositoryImpl(jpaQuestionRepository); + } + + @Test + void 질문을_저장한다() { + // given + final Question question = new Question(경매, 질문자, 질문_내용); + + // when + final Question actual = questionRepository.save(question); + + // then + assertThat(actual.getId()).isPositive(); + } + + @Test + void 삭제된_질문은_조회되지_않는다() { + // when + final Optional actual = questionRepository.findById(삭제된_질문.getId()); + + // then + assertThat(actual).isEmpty(); + } + + @Test + void 삭제되지_않은_질문은_조회된다() { + // when + final Optional actual = questionRepository.findById(질문1.getId()); + + // then + assertThat(actual).contains(질문1); + } + + @Test + void 경매_아이디를_통해_질문과_답변들을_모두_조회한다() { + // when + final List actual = questionRepository.findAllByAuctionId(질문이_3개_답변이_2개인_경매.getId()); + + // then + SoftAssertions.assertSoftly(softAssertions -> { + softAssertions.assertThat(actual).hasSize(3); + softAssertions.assertThat(actual.get(0)).isEqualTo(질문1); + softAssertions.assertThat(actual.get(0).getAnswer()).isEqualTo(답변1); + softAssertions.assertThat(actual.get(1)).isEqualTo(질문2); + softAssertions.assertThat(actual.get(1).getAnswer()).isEqualTo(답변2); + softAssertions.assertThat(actual.get(2)).isEqualTo(질문3); + softAssertions.assertThat(actual.get(2).getAnswer()).isNull(); + }); + } +} diff --git a/backend/ddang/src/test/java/com/ddang/ddang/qna/infrastructure/fixture/AnswerRepositoryImplFixture.java b/backend/ddang/src/test/java/com/ddang/ddang/qna/infrastructure/fixture/AnswerRepositoryImplFixture.java new file mode 100644 index 000000000..9a829351c --- /dev/null +++ b/backend/ddang/src/test/java/com/ddang/ddang/qna/infrastructure/fixture/AnswerRepositoryImplFixture.java @@ -0,0 +1,103 @@ +package com.ddang.ddang.qna.infrastructure.fixture; + +import com.ddang.ddang.auction.domain.Auction; +import com.ddang.ddang.auction.domain.BidUnit; +import com.ddang.ddang.auction.domain.Price; +import com.ddang.ddang.auction.domain.repository.AuctionRepository; +import com.ddang.ddang.auction.infrastructure.persistence.AuctionRepositoryImpl; +import com.ddang.ddang.auction.infrastructure.persistence.JpaAuctionRepository; +import com.ddang.ddang.auction.infrastructure.persistence.QuerydslAuctionRepository; +import com.ddang.ddang.image.domain.ProfileImage; +import com.ddang.ddang.qna.domain.Answer; +import com.ddang.ddang.qna.domain.Question; +import com.ddang.ddang.qna.domain.repository.AnswerRepository; +import com.ddang.ddang.qna.domain.repository.QuestionRepository; +import com.ddang.ddang.qna.infrastructure.AnswerRepositoryImpl; +import com.ddang.ddang.qna.infrastructure.JpaAnswerRepository; +import com.ddang.ddang.qna.infrastructure.JpaQuestionRepository; +import com.ddang.ddang.qna.infrastructure.QuestionRepositoryImpl; +import com.ddang.ddang.user.domain.Reliability; +import com.ddang.ddang.user.domain.User; +import com.ddang.ddang.user.domain.repository.UserRepository; +import com.ddang.ddang.user.infrastructure.persistence.JpaUserRepository; +import com.ddang.ddang.user.infrastructure.persistence.UserRepositoryImpl; +import com.querydsl.jpa.impl.JPAQueryFactory; +import org.junit.jupiter.api.BeforeEach; +import org.springframework.beans.factory.annotation.Autowired; + +import java.time.LocalDateTime; + +@SuppressWarnings("NonAsciiCharacters") +public class AnswerRepositoryImplFixture { + + private UserRepository userRepository; + + private AuctionRepository auctionRepository; + + private QuestionRepository questionRepository; + + private AnswerRepository answerRepository; + + protected User 판매자; + protected Question 질문; + protected Question 답변이_존재하는_질문; + protected Question 답변이_존재하지_않는_질문; + protected String 답변_내용 = "답변드립니다."; + protected Answer 답변; + protected Answer 삭제된_답변; + + @BeforeEach + void setUpFixture( + @Autowired final JPAQueryFactory jpaQueryFactory, + @Autowired final JpaUserRepository jpaUserRepository, + @Autowired final JpaAuctionRepository jpaAuctionRepository, + @Autowired final JpaQuestionRepository jpaQuestionRepository, + @Autowired final JpaAnswerRepository jpaAnswerRepository + ) { + userRepository = new UserRepositoryImpl(jpaUserRepository); + auctionRepository = new AuctionRepositoryImpl(jpaAuctionRepository, new QuerydslAuctionRepository(jpaQueryFactory)); + questionRepository = new QuestionRepositoryImpl(jpaQuestionRepository); + answerRepository = new AnswerRepositoryImpl(jpaAnswerRepository); + + final ProfileImage 프로필_이미지 = new ProfileImage("프로필.jpg", "프로필.jpg"); + 판매자 = User.builder() + .name("판매자") + .profileImage(프로필_이미지) + .reliability(new Reliability(4.7d)) + .oauthId("12345") + .build(); + final Auction 경매 = Auction.builder() + .seller(판매자) + .title("경매 상품 1") + .description("이것은 경매 상품 1 입니다.") + .bidUnit(new BidUnit(1_000)) + .startPrice(new Price(1_000)) + .closingTime(LocalDateTime.now()) + .build(); + final User 질문자 = User.builder() + .name("질문자") + .profileImage(프로필_이미지) + .reliability(new Reliability(4.7d)) + .oauthId("12346") + .build(); + 질문 = new Question(경매, 질문자, "궁금한 점이 있어요."); + 답변이_존재하는_질문 = new Question(경매, 질문자, "궁금한 점이 있어요."); + 답변이_존재하지_않는_질문 = 질문; + final Question 답변이_삭제된_질문 = new Question(경매, 질문자, "궁금한 점이 있어요."); + + 답변 = new Answer(판매자, "답변드립니다."); + 답변이_존재하는_질문.addAnswer(답변); + 삭제된_답변 = new Answer(판매자, "답변드립니다."); + 답변이_삭제된_질문.addAnswer(삭제된_답변); + 삭제된_답변.delete(); + + userRepository.save(판매자); + userRepository.save(질문자); + auctionRepository.save(경매); + questionRepository.save(질문); + questionRepository.save(답변이_존재하는_질문); + questionRepository.save(답변이_삭제된_질문); + answerRepository.save(답변); + answerRepository.save(삭제된_답변); + } +} diff --git a/backend/ddang/src/test/java/com/ddang/ddang/qna/infrastructure/fixture/JpaAnswerRepositoryFixture.java b/backend/ddang/src/test/java/com/ddang/ddang/qna/infrastructure/fixture/JpaAnswerRepositoryFixture.java index 524ef1cae..9bf067ef2 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/qna/infrastructure/fixture/JpaAnswerRepositoryFixture.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/qna/infrastructure/fixture/JpaAnswerRepositoryFixture.java @@ -36,8 +36,9 @@ public class JpaAnswerRepositoryFixture { private JpaQuestionRepository questionRepository; @Autowired - private JpaAnswerRepository answerRepository; + private JpaAnswerRepository answerRepository; + protected User 판매자; protected Question 질문; protected Question 답변이_존재하는_질문; protected Question 답변이_존재하지_않는_질문; @@ -48,12 +49,12 @@ public class JpaAnswerRepositoryFixture { @BeforeEach void setUp() { final ProfileImage 프로필_이미지 = new ProfileImage("프로필.jpg", "프로필.jpg"); - final User 판매자 = User.builder() - .name("판매자") - .profileImage(프로필_이미지) - .reliability(new Reliability(4.7d)) - .oauthId("12345") - .build(); + 판매자 = User.builder() + .name("판매자") + .profileImage(프로필_이미지) + .reliability(new Reliability(4.7d)) + .oauthId("12345") + .build(); final Auction 경매 = Auction.builder() .seller(판매자) .title("경매 상품 1") @@ -73,9 +74,9 @@ void setUp() { 답변이_존재하지_않는_질문 = 질문; final Question 답변이_삭제된_질문 = new Question(경매, 질문자, "궁금한 점이 있어요."); - 답변 = new Answer("답변드립니다."); + 답변 = new Answer(판매자, "답변드립니다."); 답변이_존재하는_질문.addAnswer(답변); - 삭제된_답변 = new Answer("답변드립니다."); + 삭제된_답변 = new Answer(판매자, "답변드립니다."); 답변이_삭제된_질문.addAnswer(삭제된_답변); 삭제된_답변.delete(); diff --git a/backend/ddang/src/test/java/com/ddang/ddang/qna/infrastructure/fixture/JpaQuestionRepositoryFixture.java b/backend/ddang/src/test/java/com/ddang/ddang/qna/infrastructure/fixture/JpaQuestionRepositoryFixture.java index 40a7ceeda..3baac995b 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/qna/infrastructure/fixture/JpaQuestionRepositoryFixture.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/qna/infrastructure/fixture/JpaQuestionRepositoryFixture.java @@ -40,14 +40,21 @@ public class JpaQuestionRepositoryFixture { protected Auction 경매; protected Auction 질문이_3개_답변이_2개인_경매; + protected Auction 질문이_3개_답변이_2개중_첫번째_질문이_삭제된_경매; + protected Auction 질문이_3개_답변이_2개중_첫번째_답변이_삭제된_경매; protected User 질문자; protected String 질문_내용 = "궁금한 점이 있어요."; protected Question 질문1; protected Question 질문2; protected Question 질문3; + protected Question 삭제된_질문1; + protected Question 삭제되지_않은_질문2; + protected Question 답변이_없는_질문3; protected Question 삭제된_질문; protected Answer 답변1; protected Answer 답변2; + protected Answer 삭제된_질문의_답변1; + protected Answer 삭제되지_않은_질문의_답변2; @BeforeEach void setUp() { @@ -74,6 +81,22 @@ void setUp() { .startPrice(new Price(1_000)) .closingTime(LocalDateTime.now()) .build(); + 질문이_3개_답변이_2개중_첫번째_질문이_삭제된_경매 = Auction.builder() + .seller(판매자) + .title("경매 상품") + .description("이것은 경매 상품입니다.") + .bidUnit(new BidUnit(1_000)) + .startPrice(new Price(1_000)) + .closingTime(LocalDateTime.now()) + .build(); + 질문이_3개_답변이_2개중_첫번째_답변이_삭제된_경매 = Auction.builder() + .seller(판매자) + .title("경매 상품") + .description("이것은 경매 상품입니다.") + .bidUnit(new BidUnit(1_000)) + .startPrice(new Price(1_000)) + .closingTime(LocalDateTime.now()) + .build(); 질문자 = User.builder() .name("질문자") .profileImage(프로필_이미지) @@ -84,18 +107,27 @@ void setUp() { 질문1 = new Question(질문이_3개_답변이_2개인_경매, 질문자, "질문1"); 질문2 = new Question(질문이_3개_답변이_2개인_경매, 질문자, "질문2"); 질문3 = new Question(질문이_3개_답변이_2개인_경매, 질문자, "질문3"); - 답변1 = new Answer("답변1"); - 답변2 = new Answer("답변2"); + 답변1 = new Answer(판매자, "답변1"); + 답변2 = new Answer(판매자, "답변2"); 질문1.addAnswer(답변1); 질문2.addAnswer(답변2); + 삭제된_질문1 = new Question(질문이_3개_답변이_2개중_첫번째_질문이_삭제된_경매, 질문자, "질문1"); + 삭제되지_않은_질문2 = new Question(질문이_3개_답변이_2개중_첫번째_질문이_삭제된_경매, 질문자, "질문2"); + 답변이_없는_질문3 = new Question(질문이_3개_답변이_2개중_첫번째_질문이_삭제된_경매, 질문자, "질문3"); + 삭제된_질문의_답변1 = new Answer(판매자, "삭제된 질문의 답변"); + 삭제되지_않은_질문의_답변2 = new Answer(판매자, "삭제되지 않은 질문의 답변2"); + 삭제된_질문1.addAnswer(삭제된_질문의_답변1); + 삭제된_질문1.delete(); + 삭제되지_않은_질문2.addAnswer(삭제되지_않은_질문의_답변2); + 삭제된_질문 = new Question(경매, 질문자, "질문3"); 삭제된_질문.delete(); userRepository.saveAll(List.of(판매자, 질문자)); - auctionRepository.saveAll(List.of(경매, 질문이_3개_답변이_2개인_경매)); - questionRepository.saveAll(List.of(질문1, 질문2, 질문3, 삭제된_질문)); - answerRepository.saveAll(List.of(답변1, 답변2)); + auctionRepository.saveAll(List.of(경매, 질문이_3개_답변이_2개인_경매, 질문이_3개_답변이_2개중_첫번째_질문이_삭제된_경매, 질문이_3개_답변이_2개중_첫번째_답변이_삭제된_경매)); + questionRepository.saveAll(List.of(질문1, 질문2, 질문3, 삭제된_질문, 삭제된_질문1, 삭제되지_않은_질문2, 답변이_없는_질문3)); + answerRepository.saveAll(List.of(답변1, 답변2, 삭제된_질문의_답변1, 삭제되지_않은_질문의_답변2)); em.flush(); em.clear(); diff --git a/backend/ddang/src/test/java/com/ddang/ddang/qna/infrastructure/fixture/QuestionRepositoryImplFixture.java b/backend/ddang/src/test/java/com/ddang/ddang/qna/infrastructure/fixture/QuestionRepositoryImplFixture.java new file mode 100644 index 000000000..2bea362bd --- /dev/null +++ b/backend/ddang/src/test/java/com/ddang/ddang/qna/infrastructure/fixture/QuestionRepositoryImplFixture.java @@ -0,0 +1,117 @@ +package com.ddang.ddang.qna.infrastructure.fixture; + +import com.ddang.ddang.auction.domain.Auction; +import com.ddang.ddang.auction.domain.BidUnit; +import com.ddang.ddang.auction.domain.Price; +import com.ddang.ddang.auction.domain.repository.AuctionRepository; +import com.ddang.ddang.auction.infrastructure.persistence.AuctionRepositoryImpl; +import com.ddang.ddang.auction.infrastructure.persistence.JpaAuctionRepository; +import com.ddang.ddang.auction.infrastructure.persistence.QuerydslAuctionRepository; +import com.ddang.ddang.image.domain.ProfileImage; +import com.ddang.ddang.qna.domain.Answer; +import com.ddang.ddang.qna.domain.Question; +import com.ddang.ddang.qna.domain.repository.AnswerRepository; +import com.ddang.ddang.qna.domain.repository.QuestionRepository; +import com.ddang.ddang.qna.infrastructure.AnswerRepositoryImpl; +import com.ddang.ddang.qna.infrastructure.JpaAnswerRepository; +import com.ddang.ddang.qna.infrastructure.JpaQuestionRepository; +import com.ddang.ddang.qna.infrastructure.QuestionRepositoryImpl; +import com.ddang.ddang.user.domain.Reliability; +import com.ddang.ddang.user.domain.User; +import com.ddang.ddang.user.domain.repository.UserRepository; +import com.ddang.ddang.user.infrastructure.persistence.JpaUserRepository; +import com.ddang.ddang.user.infrastructure.persistence.UserRepositoryImpl; +import com.querydsl.jpa.impl.JPAQueryFactory; +import org.junit.jupiter.api.BeforeEach; +import org.springframework.beans.factory.annotation.Autowired; + +import java.time.LocalDateTime; + +@SuppressWarnings("NonAsciiCharacters") +public class QuestionRepositoryImplFixture { + + private AuctionRepository auctionRepository; + + private UserRepository userRepository; + + private QuestionRepository questionRepository; + + private AnswerRepository answerRepository; + + protected Auction 경매; + protected Auction 질문이_3개_답변이_2개인_경매; + protected User 질문자; + protected String 질문_내용 = "궁금한 점이 있어요."; + protected Question 질문1; + protected Question 질문2; + protected Question 질문3; + protected Question 삭제된_질문; + protected Answer 답변1; + protected Answer 답변2; + + @BeforeEach + void setUpFixture( + @Autowired final JpaAuctionRepository jpaAuctionRepository, + @Autowired final JPAQueryFactory queryFactory, + @Autowired final JpaUserRepository jpaUserRepository, + @Autowired final JpaQuestionRepository jpaQuestionRepository, + @Autowired final JpaAnswerRepository jpaAnswerRepository + ) { + auctionRepository = new AuctionRepositoryImpl(jpaAuctionRepository, new QuerydslAuctionRepository(queryFactory)); + userRepository = new UserRepositoryImpl(jpaUserRepository); + questionRepository = new QuestionRepositoryImpl(jpaQuestionRepository); + answerRepository = new AnswerRepositoryImpl(jpaAnswerRepository); + + final ProfileImage 프로필_이미지 = new ProfileImage("프로필.jpg", "프로필.jpg"); + final User 판매자 = User.builder() + .name("판매자") + .profileImage(프로필_이미지) + .reliability(new Reliability(4.7d)) + .oauthId("12345") + .build(); + 경매 = Auction.builder() + .seller(판매자) + .title("경매 상품") + .description("이것은 경매 상품입니다.") + .bidUnit(new BidUnit(1_000)) + .startPrice(new Price(1_000)) + .closingTime(LocalDateTime.now()) + .build(); + 질문이_3개_답변이_2개인_경매 = Auction.builder() + .seller(판매자) + .title("경매 상품") + .description("이것은 경매 상품입니다.") + .bidUnit(new BidUnit(1_000)) + .startPrice(new Price(1_000)) + .closingTime(LocalDateTime.now()) + .build(); + 질문자 = User.builder() + .name("질문자") + .profileImage(프로필_이미지) + .reliability(new Reliability(4.7d)) + .oauthId("12346") + .build(); + + 질문1 = new Question(질문이_3개_답변이_2개인_경매, 질문자, "질문1"); + 질문2 = new Question(질문이_3개_답변이_2개인_경매, 질문자, "질문2"); + 질문3 = new Question(질문이_3개_답변이_2개인_경매, 질문자, "질문3"); + 답변1 = new Answer(판매자, "답변1"); + 답변2 = new Answer(판매자, "답변2"); + 질문1.addAnswer(답변1); + 질문2.addAnswer(답변2); + + 삭제된_질문 = new Question(경매, 질문자, "질문3"); + 삭제된_질문.delete(); + + userRepository.save(판매자); + userRepository.save(질문자); + auctionRepository.save(경매); + auctionRepository.save(질문이_3개_답변이_2개인_경매); + questionRepository.save(질문1); + questionRepository.save(질문2); + questionRepository.save(질문3); + questionRepository.save(삭제된_질문); + answerRepository.save(답변1); + answerRepository.save(답변2); + } +} diff --git a/backend/ddang/src/test/java/com/ddang/ddang/qna/presentation/QnaControllerTest.java b/backend/ddang/src/test/java/com/ddang/ddang/qna/presentation/QnaControllerTest.java index df9614eff..fef77aefc 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/qna/presentation/QnaControllerTest.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/qna/presentation/QnaControllerTest.java @@ -92,7 +92,7 @@ void setUp() { void 질문을_등록한다() throws Exception { // given given(tokenDecoder.decode(eq(TokenType.ACCESS), anyString())).willReturn(Optional.of(사용자_ID_클레임)); - given(questionService.create(any(CreateQuestionDto.class))).willReturn(생성된_질문_아이디); + given(questionService.create(any(CreateQuestionDto.class), anyString())).willReturn(생성된_질문_아이디); // when & then final ResultActions resultActions = mockMvc.perform(post("/questions") @@ -168,7 +168,7 @@ private static Stream provideQuestionRequestWithEmptyCont void 존재하지_않은_사용자가_질문시_404를_반환한다() throws Exception { // given given(tokenDecoder.decode(eq(TokenType.ACCESS), anyString())).willReturn(Optional.of(존재하지_않는_사용자_ID_클레임)); - given(questionService.create(any(CreateQuestionDto.class))) + given(questionService.create(any(CreateQuestionDto.class), anyString())) .willThrow(new UserNotFoundException("해당 사용자를 찾을 수 없습니다.")); // when & then @@ -187,7 +187,7 @@ private static Stream provideQuestionRequestWithEmptyCont void 존재하지_않은_경매에_질문시_404를_반환한다() throws Exception { // given given(tokenDecoder.decode(eq(TokenType.ACCESS), anyString())).willReturn(Optional.of(사용자_ID_클레임)); - given(questionService.create(any(CreateQuestionDto.class))) + given(questionService.create(any(CreateQuestionDto.class), anyString())) .willThrow(new AuctionNotFoundException("해당 경매를 찾을 수 없습니다.")); // when & then @@ -206,7 +206,7 @@ private static Stream provideQuestionRequestWithEmptyCont void 이미_종료된_경매에_질문시_400을_반환한다() throws Exception { // given given(tokenDecoder.decode(eq(TokenType.ACCESS), anyString())).willReturn(Optional.of(사용자_ID_클레임)); - given(questionService.create(any(CreateQuestionDto.class))) + given(questionService.create(any(CreateQuestionDto.class), anyString())) .willThrow(new InvalidAuctionToAskQuestionException("이미 종료된 경매입니다")); // when & then @@ -225,7 +225,7 @@ private static Stream provideQuestionRequestWithEmptyCont void 삭제된_경매에_질문시_400을_반환한다() throws Exception { // given given(tokenDecoder.decode(eq(TokenType.ACCESS), anyString())).willReturn(Optional.of(사용자_ID_클레임)); - given(questionService.create(any(CreateQuestionDto.class))) + given(questionService.create(any(CreateQuestionDto.class), anyString())) .willThrow(new InvalidAuctionToAskQuestionException("삭제된 경매입니다")); // when & then @@ -244,7 +244,7 @@ private static Stream provideQuestionRequestWithEmptyCont void 경매자가_본인이_등록한_경매에_질문시_400을_반환한다() throws Exception { // given given(tokenDecoder.decode(eq(TokenType.ACCESS), anyString())).willReturn(Optional.of(사용자_ID_클레임)); - given(questionService.create(any(CreateQuestionDto.class))) + given(questionService.create(any(CreateQuestionDto.class), anyString())) .willThrow(new InvalidQuestionerException("경매 등록자는 질문할 수 없습니다")); // when & then @@ -263,7 +263,7 @@ private static Stream provideQuestionRequestWithEmptyCont void 답변을_등록한다() throws Exception { // given given(tokenDecoder.decode(eq(TokenType.ACCESS), anyString())).willReturn(Optional.of(사용자_ID_클레임)); - given(answerService.create(any(CreateAnswerDto.class))).willReturn(생성된_답변_아이디); + given(answerService.create(any(CreateAnswerDto.class), anyString())).willReturn(생성된_답변_아이디); // when & then final ResultActions resultActions = @@ -340,7 +340,7 @@ private static Stream provideAnswerRequestWithEmptyContent( void 존재하지_않은_사용자가_답변시_404을_반환한다() throws Exception { // given given(tokenDecoder.decode(eq(TokenType.ACCESS), anyString())).willReturn(Optional.of(존재하지_않는_사용자_ID_클레임)); - given(answerService.create(any(CreateAnswerDto.class))) + given(answerService.create(any(CreateAnswerDto.class), anyString())) .willThrow(new UserNotFoundException("해당 사용자를 찾을 수 없습니다.")); // when & then @@ -359,7 +359,7 @@ private static Stream provideAnswerRequestWithEmptyContent( void 존재하지_않은_질문에_답변시_404을_반환한다() throws Exception { // given given(tokenDecoder.decode(eq(TokenType.ACCESS), anyString())).willReturn(Optional.of(사용자_ID_클레임)); - given(answerService.create(any(CreateAnswerDto.class))) + given(answerService.create(any(CreateAnswerDto.class), anyString())) .willThrow(new QuestionNotFoundException("해당 질문을 찾을 수 없습니다.")); // when & then @@ -378,7 +378,7 @@ private static Stream provideAnswerRequestWithEmptyContent( void 판매자가_아닌_사용자가_질문에_답변시_400을_반환한다() throws Exception { // given given(tokenDecoder.decode(eq(TokenType.ACCESS), anyString())).willReturn(Optional.of(사용자_ID_클레임)); - given(answerService.create(any(CreateAnswerDto.class))) + given(answerService.create(any(CreateAnswerDto.class), anyString())) .willThrow(new InvalidAnswererException("판매자만 답변할 수 있습니다.")); // when & then @@ -397,7 +397,7 @@ private static Stream provideAnswerRequestWithEmptyContent( void 이미_답변한_질문에_답변시_400을_반환한다() throws Exception { // given given(tokenDecoder.decode(eq(TokenType.ACCESS), anyString())).willReturn(Optional.of(사용자_ID_클레임)); - given(answerService.create(any(CreateAnswerDto.class))) + given(answerService.create(any(CreateAnswerDto.class), anyString())) .willThrow(new AlreadyAnsweredException("이미 답변한 질문입니다.")); // when & then diff --git a/backend/ddang/src/test/java/com/ddang/ddang/qna/presentation/fixture/QnaControllerFixture.java b/backend/ddang/src/test/java/com/ddang/ddang/qna/presentation/fixture/QnaControllerFixture.java index 51e7dd519..5d80b44b1 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/qna/presentation/fixture/QnaControllerFixture.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/qna/presentation/fixture/QnaControllerFixture.java @@ -17,6 +17,7 @@ public class QnaControllerFixture extends CommonControllerSliceTest { protected Long 존재하지_않는_질문_아이디 = 999L; protected Long 존재하지_않는_답변_아이디 = 999L; protected String 액세스_토큰_값 = "Bearer accessToken"; + protected String 이미지_절대_경로 = "/imageUrl"; protected CreateQuestionRequest 질문_등록_request = new CreateQuestionRequest(1L, "궁금한 점이 있습니다."); protected CreateQuestionRequest 경매_아이디가_없는_질문_등록_request = new CreateQuestionRequest(null, "궁금한 점이 있습니다."); diff --git a/backend/ddang/src/test/java/com/ddang/ddang/region/application/RegionServiceTest.java b/backend/ddang/src/test/java/com/ddang/ddang/region/application/RegionServiceTest.java index 017f1bca1..58549f268 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/region/application/RegionServiceTest.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/region/application/RegionServiceTest.java @@ -5,7 +5,7 @@ import com.ddang.ddang.region.application.fixture.RegionServiceFixture; import com.ddang.ddang.region.domain.InitializationRegionProcessor; import com.ddang.ddang.region.domain.Region; -import com.ddang.ddang.region.infrastructure.persistence.JpaRegionRepository; +import com.ddang.ddang.region.domain.repository.RegionRepository; import org.assertj.core.api.SoftAssertions; import org.junit.jupiter.api.DisplayNameGeneration; import org.junit.jupiter.api.DisplayNameGenerator; @@ -15,6 +15,7 @@ import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.transaction.annotation.Transactional; +import java.util.ArrayList; import java.util.List; import static org.assertj.core.api.Assertions.assertThatThrownBy; @@ -33,7 +34,7 @@ class RegionServiceTest extends RegionServiceFixture { RegionService regionService; @Autowired - JpaRegionRepository regionRepository; + RegionRepository regionRepository; @Test void 대한민국_전국의_지역을_초기화한다() { @@ -44,25 +45,29 @@ class RegionServiceTest extends RegionServiceFixture { regionService.createRegions(); // then - final List actual = regionRepository.findAll(); + final List actualAllFirsts = regionRepository.findFirstAll(); - final Region actualFirstRegion1 = actual.get(0); - final Region actualFirstRegion2 = actual.get(5); - final Region actualSecondRegion1OfFirstRegion1 = actualFirstRegion1.getSecondRegions().get(0); - final Region actualSecondRegion2OfFirstRegion1 = actualFirstRegion1.getSecondRegions().get(1); - final Region actualThirdRegion1OfSecondRegion1 = actualSecondRegion1OfFirstRegion1.getThirdRegions().get(0); - final Region actualThirdRegion2OfSecondRegion1 = actualSecondRegion1OfFirstRegion1.getThirdRegions().get(1); + List actualAllSeconds = new ArrayList<>(); + for (final Region first : actualAllFirsts) { + actualAllSeconds.addAll(regionRepository.findSecondAllByFirstRegionId(first.getId())); + } + + List actualAllThirds = new ArrayList<>(); + for (final Region second : actualAllSeconds) { + final Region first = second.getFirstRegion(); + actualAllThirds.addAll(regionRepository.findThirdAllByFirstAndSecondRegionId(first.getId(), second.getId())); + } SoftAssertions.assertSoftly(softAssertions -> { - softAssertions.assertThat(actual).hasSize(6); - softAssertions.assertThat(actualFirstRegion1).isEqualTo(서울특별시); - softAssertions.assertThat(actualFirstRegion2).isEqualTo(두번째_지역이_없는_첫번째_지역); - softAssertions.assertThat(actualFirstRegion1.getSecondRegions()).hasSize(2); - softAssertions.assertThat(actualSecondRegion1OfFirstRegion1).isEqualTo(서울특별시_강남구); - softAssertions.assertThat(actualSecondRegion2OfFirstRegion1).isEqualTo(세번째_지역이_없는_두번째_지역); - softAssertions.assertThat(actualSecondRegion1OfFirstRegion1.getThirdRegions()).hasSize(2); - softAssertions.assertThat(actualThirdRegion1OfSecondRegion1).isEqualTo(서울특별시_강남구_삼성동); - softAssertions.assertThat(actualThirdRegion2OfSecondRegion1).isEqualTo(서울특별시_강남구_대치동); + softAssertions.assertThat(actualAllFirsts).hasSize(2); + softAssertions.assertThat(actualAllSeconds).hasSize(2); + softAssertions.assertThat(actualAllThirds).hasSize(2); + softAssertions.assertThat(actualAllFirsts.get(0)).isEqualTo(서울특별시); + softAssertions.assertThat(actualAllFirsts.get(1)).isEqualTo(두번째_지역이_없는_첫번째_지역); + softAssertions.assertThat(actualAllSeconds.get(0)).isEqualTo(서울특별시_강남구); + softAssertions.assertThat(actualAllSeconds.get(1)).isEqualTo(세번째_지역이_없는_두번째_지역); + softAssertions.assertThat(actualAllThirds.get(0)).isEqualTo(서울특별시_강남구_삼성동); + softAssertions.assertThat(actualAllThirds.get(1)).isEqualTo(서울특별시_강남구_대치동); }); } @@ -103,7 +108,7 @@ class RegionServiceTest extends RegionServiceFixture { @Test void 두번째_지역에_해당하는_모든_세번째_지역을_조회한다() { // when - final List actual = + final List actual = regionService.readAllThirdByFirstAndSecondRegionId(서울특별시.getId(), 서울특별시_강남구.getId()); // then diff --git a/backend/ddang/src/test/java/com/ddang/ddang/region/application/fixture/RegionServiceFixture.java b/backend/ddang/src/test/java/com/ddang/ddang/region/application/fixture/RegionServiceFixture.java index 4a55a76e9..dc3d8a04c 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/region/application/fixture/RegionServiceFixture.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/region/application/fixture/RegionServiceFixture.java @@ -1,7 +1,7 @@ package com.ddang.ddang.region.application.fixture; import com.ddang.ddang.region.domain.Region; -import com.ddang.ddang.region.infrastructure.persistence.JpaRegionRepository; +import com.ddang.ddang.region.domain.repository.RegionRepository; import org.junit.jupiter.api.BeforeEach; import org.springframework.beans.factory.annotation.Autowired; @@ -9,7 +9,7 @@ public class RegionServiceFixture { @Autowired - private JpaRegionRepository regionRepository; + private RegionRepository regionRepository; protected Region 서울특별시; protected Region 두번째_지역이_없는_첫번째_지역; diff --git a/backend/ddang/src/test/java/com/ddang/ddang/region/infrastructure/persistence/JpaRegionRepositoryTest.java b/backend/ddang/src/test/java/com/ddang/ddang/region/infrastructure/persistence/JpaRegionRepositoryTest.java index 2461e0ac3..0bbb9b402 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/region/infrastructure/persistence/JpaRegionRepositoryTest.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/region/infrastructure/persistence/JpaRegionRepositoryTest.java @@ -25,6 +25,34 @@ class JpaRegionRepositoryTest extends JpaRegionRepositoryFixture { @Autowired JpaRegionRepository regionRepository; + @Test + void 지역을_저장한다() { + // given + final Region region = new Region("region1"); + + // when + final Region actual = regionRepository.save(region); + + // then + assertThat(actual.getId()).isPositive(); + } + + @Test + void 지역을_여러개_한번에_저장한다() { + // given + final Region region1 = new Region("region1"); + final Region region2 = new Region("region2"); + + // when + final List actual = regionRepository.saveAll(List.of(region1, region2)); + + // then + SoftAssertions.assertSoftly(softAssertions -> { + softAssertions.assertThat(actual.get(0).getId()).isPositive(); + softAssertions.assertThat(actual.get(1).getId()).isPositive(); + }); + } + @Test void 모든_첫번째_지역을_조회한다() { // when diff --git a/backend/ddang/src/test/java/com/ddang/ddang/region/infrastructure/persistence/RegionRepositoryImplTest.java b/backend/ddang/src/test/java/com/ddang/ddang/region/infrastructure/persistence/RegionRepositoryImplTest.java new file mode 100644 index 000000000..fb4be9bdd --- /dev/null +++ b/backend/ddang/src/test/java/com/ddang/ddang/region/infrastructure/persistence/RegionRepositoryImplTest.java @@ -0,0 +1,142 @@ +package com.ddang.ddang.region.infrastructure.persistence; + +import com.ddang.ddang.configuration.QuerydslConfiguration; +import com.ddang.ddang.region.domain.Region; +import com.ddang.ddang.region.domain.repository.RegionRepository; +import com.ddang.ddang.region.infrastructure.persistence.fixture.RegionRepositoryImplFixture; +import org.assertj.core.api.SoftAssertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.context.annotation.Import; + +import java.util.List; +import java.util.Optional; + +import static org.assertj.core.api.Assertions.assertThat; + +@DataJpaTest +@Import(QuerydslConfiguration.class) +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) +@SuppressWarnings("NonAsciiCharacters") +class RegionRepositoryImplTest extends RegionRepositoryImplFixture { + + RegionRepository regionRepository; + + @BeforeEach + void setUp(@Autowired final JpaRegionRepository jpaRegionRepository) { + regionRepository = new RegionRepositoryImpl(jpaRegionRepository); + } + + @Test + void 지역을_저장한다() { + // given + final Region region = new Region("region1"); + + // when + final Region actual = regionRepository.save(region); + + // then + assertThat(actual.getId()).isPositive(); + } + + @Test + void 지역을_여러개_한번에_저장한다() { + // given + final Region region1 = new Region("region1"); + final Region region2 = new Region("region2"); + + // when + final List actual = regionRepository.saveAll(List.of(region1, region2)); + + // then + SoftAssertions.assertSoftly(softAssertions -> { + softAssertions.assertThat(actual.get(0).getId()).isPositive(); + softAssertions.assertThat(actual.get(1).getId()).isPositive(); + }); + } + + @Test + void 모든_첫번째_지역을_조회한다() { + // when + final List actual = regionRepository.findFirstAll(); + + // then + SoftAssertions.assertSoftly(softAssertions -> { + softAssertions.assertThat(actual).hasSize(2); + softAssertions.assertThat(actual.get(0)).isEqualTo(서울특별시); + softAssertions.assertThat(actual.get(1)).isEqualTo(경기도); + }); + } + + @Test + void 첫번째_지역에_해당하는_모든_두번째_지역을_조회한다() { + // when + final List actual = regionRepository.findSecondAllByFirstRegionId(서울특별시.getId()); + + // then + SoftAssertions.assertSoftly(softAssertions -> { + softAssertions.assertThat(actual).hasSize(2); + softAssertions.assertThat(actual.get(0)).isEqualTo(서울특별시_강남구); + softAssertions.assertThat(actual.get(1)).isEqualTo(서울특별시_송파구); + }); + } + + @Test + void 두번째_지역에_해당하는_모든_세번째_지역을_조회한다() { + // when + final List actual = + regionRepository.findThirdAllByFirstAndSecondRegionId(서울특별시.getId(), 서울특별시_강남구.getId()); + + // then + SoftAssertions.assertSoftly(softAssertions -> { + softAssertions.assertThat(actual).hasSize(2); + softAssertions.assertThat(actual.get(0)).isEqualTo(서울특별시_강남구_삼성동); + softAssertions.assertThat(actual.get(1)).isEqualTo(서울특별시_강남구_대치동); + }); + } + + @Test + void 세번째_지역을_조회한다() { + // when + final Optional actual = regionRepository.findThirdRegionById(서울특별시_강남구_삼성동.getId()); + + // then + assertThat(actual).contains(서울특별시_강남구_삼성동); + } + + @Test + void 세번째_지역이_아닌_아이디를_전달하면_빈_Optional을_반환한다() { + // when + final Optional actual = regionRepository.findThirdRegionById(서울특별시.getId()); + + // then + assertThat(actual).isEmpty(); + } + + @Test + void 세번째_지역에_해당하는_모든_id를_전달하면_그에_맞는_thirdRegions를_반환한다() { + // when + final List thirdRegionIds = List.of(서울특별시_강남구_삼성동.getId(), 서울특별시_강남구_대치동.getId()); + final List actual = regionRepository.findAllThirdRegionByIds(thirdRegionIds); + + // then + SoftAssertions.assertSoftly(softAssertions -> { + softAssertions.assertThat(actual).hasSize(2); + softAssertions.assertThat(actual.get(0)).isEqualTo(서울특별시_강남구_삼성동); + softAssertions.assertThat(actual.get(1)).isEqualTo(서울특별시_강남구_대치동); + }); + } + + @Test + void 세번째_지역이_아닌_아이디를_전달하면_빈_List를_반환한다() { + // when + final List actual = regionRepository.findAllThirdRegionByIds(List.of(서울특별시.getId())); + + // then + assertThat(actual).isEmpty(); + } +} diff --git a/backend/ddang/src/test/java/com/ddang/ddang/region/infrastructure/persistence/fixture/RegionRepositoryImplFixture.java b/backend/ddang/src/test/java/com/ddang/ddang/region/infrastructure/persistence/fixture/RegionRepositoryImplFixture.java new file mode 100644 index 000000000..3226cced4 --- /dev/null +++ b/backend/ddang/src/test/java/com/ddang/ddang/region/infrastructure/persistence/fixture/RegionRepositoryImplFixture.java @@ -0,0 +1,42 @@ +package com.ddang.ddang.region.infrastructure.persistence.fixture; + +import com.ddang.ddang.region.domain.Region; +import com.ddang.ddang.region.domain.repository.RegionRepository; +import com.ddang.ddang.region.infrastructure.persistence.JpaRegionRepository; +import com.ddang.ddang.region.infrastructure.persistence.RegionRepositoryImpl; +import org.junit.jupiter.api.BeforeEach; +import org.springframework.beans.factory.annotation.Autowired; + +@SuppressWarnings("NonAsciiCharacters") +public class RegionRepositoryImplFixture { + + private RegionRepository regionRepository; + + protected Region 서울특별시; + protected Region 경기도; + protected Region 서울특별시_강남구; + protected Region 서울특별시_송파구; + protected Region 서울특별시_강남구_삼성동; + protected Region 서울특별시_강남구_대치동; + + @BeforeEach + void fixtureSetUp(@Autowired final JpaRegionRepository jpaRegionRepository) { + regionRepository = new RegionRepositoryImpl(jpaRegionRepository); + + 서울특별시 = new Region("서울특별시"); + 경기도 = new Region("경기도"); + 서울특별시_강남구 = new Region("강남구"); + 서울특별시_송파구 = new Region("송파구"); + 서울특별시_강남구_삼성동 = new Region("삼성동"); + 서울특별시_강남구_대치동 = new Region("대치동"); + + 서울특별시.addSecondRegion(서울특별시_강남구); + 서울특별시.addSecondRegion(서울특별시_송파구); + + 서울특별시_강남구.addThirdRegion(서울특별시_강남구_삼성동); + 서울특별시_강남구.addThirdRegion(서울특별시_강남구_대치동); + + regionRepository.save(서울특별시); + regionRepository.save(경기도); + } +} diff --git a/backend/ddang/src/test/java/com/ddang/ddang/report/application/AuctionReportServiceTest.java b/backend/ddang/src/test/java/com/ddang/ddang/report/application/AuctionReportServiceTest.java index e754fa7a6..9a133a9af 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/report/application/AuctionReportServiceTest.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/report/application/AuctionReportServiceTest.java @@ -1,24 +1,22 @@ package com.ddang.ddang.report.application; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + import com.ddang.ddang.auction.application.exception.AuctionNotFoundException; import com.ddang.ddang.configuration.IsolateDatabase; import com.ddang.ddang.report.application.dto.ReadAuctionReportDto; import com.ddang.ddang.report.application.exception.AlreadyReportAuctionException; -import com.ddang.ddang.report.application.exception.InvalidReportAuctionException; import com.ddang.ddang.report.application.exception.InvalidReporterToAuctionException; import com.ddang.ddang.report.application.fixture.AuctionReportServiceFixture; import com.ddang.ddang.user.application.exception.UserNotFoundException; +import java.util.List; import org.assertj.core.api.SoftAssertions; import org.junit.jupiter.api.DisplayNameGeneration; import org.junit.jupiter.api.DisplayNameGenerator; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -import java.util.List; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; - @IsolateDatabase @DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) @SuppressWarnings("NonAsciiCharacters") @@ -64,8 +62,8 @@ class AuctionReportServiceTest extends AuctionReportServiceFixture { void 삭제한_경매를_신고하는_경우_예외가_발생한다() { // when & then assertThatThrownBy(() -> auctionReportService.create(삭제된_경매_신고_요청_dto)) - .isInstanceOf(InvalidReportAuctionException.class) - .hasMessage("이미 삭제된 경매입니다."); + .isInstanceOf(AuctionNotFoundException.class) + .hasMessage("해당 경매를 찾을 수 없습니다."); } @Test diff --git a/backend/ddang/src/test/java/com/ddang/ddang/report/application/QuestionReportServiceTest.java b/backend/ddang/src/test/java/com/ddang/ddang/report/application/QuestionReportServiceTest.java index aea629942..659382b9e 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/report/application/QuestionReportServiceTest.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/report/application/QuestionReportServiceTest.java @@ -6,7 +6,7 @@ import com.ddang.ddang.report.application.exception.InvalidQuestionReportException; import com.ddang.ddang.report.application.fixture.QuestionReportServiceFixture; import com.ddang.ddang.user.application.exception.UserNotFoundException; -import org.assertj.core.api.*; +import org.assertj.core.api.SoftAssertions; import org.junit.jupiter.api.DisplayNameGeneration; import org.junit.jupiter.api.DisplayNameGenerator; import org.junit.jupiter.api.Test; diff --git a/backend/ddang/src/test/java/com/ddang/ddang/report/application/fixture/AnswerReportServiceFixture.java b/backend/ddang/src/test/java/com/ddang/ddang/report/application/fixture/AnswerReportServiceFixture.java index 82a5494b0..7d5fabb84 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/report/application/fixture/AnswerReportServiceFixture.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/report/application/fixture/AnswerReportServiceFixture.java @@ -3,21 +3,21 @@ import com.ddang.ddang.auction.domain.Auction; import com.ddang.ddang.auction.domain.BidUnit; import com.ddang.ddang.auction.domain.Price; -import com.ddang.ddang.auction.infrastructure.persistence.JpaAuctionRepository; +import com.ddang.ddang.auction.domain.repository.AuctionRepository; import com.ddang.ddang.category.domain.Category; import com.ddang.ddang.category.infrastructure.persistence.JpaCategoryRepository; import com.ddang.ddang.image.domain.AuctionImage; import com.ddang.ddang.image.domain.ProfileImage; import com.ddang.ddang.qna.domain.Answer; import com.ddang.ddang.qna.domain.Question; -import com.ddang.ddang.qna.infrastructure.JpaAnswerRepository; -import com.ddang.ddang.qna.infrastructure.JpaQuestionRepository; +import com.ddang.ddang.qna.domain.repository.AnswerRepository; +import com.ddang.ddang.qna.domain.repository.QuestionRepository; import com.ddang.ddang.report.application.dto.CreateAnswerReportDto; import com.ddang.ddang.report.domain.AnswerReport; -import com.ddang.ddang.report.infrastructure.persistence.JpaAnswerReportRepository; +import com.ddang.ddang.report.domain.repository.AnswerReportRepository; import com.ddang.ddang.user.domain.Reliability; import com.ddang.ddang.user.domain.User; -import com.ddang.ddang.user.infrastructure.persistence.JpaUserRepository; +import com.ddang.ddang.user.domain.repository.UserRepository; import org.junit.jupiter.api.BeforeEach; import org.springframework.beans.factory.annotation.Autowired; @@ -31,19 +31,19 @@ public class AnswerReportServiceFixture { private JpaCategoryRepository categoryRepository; @Autowired - private JpaUserRepository userRepository; + private UserRepository userRepository; @Autowired - private JpaAuctionRepository auctionRepository; + private AuctionRepository auctionRepository; @Autowired - private JpaQuestionRepository questionRepository; + private QuestionRepository questionRepository; @Autowired - private JpaAnswerRepository answerRepository; + private AnswerRepository answerRepository; @Autowired - private JpaAnswerReportRepository answerReportRepository; + private AnswerReportRepository answerReportRepository; protected User 신고자; protected User 이미_신고한_신고자1; @@ -119,18 +119,27 @@ void setUp() { 경매.addAuctionImages(List.of(경매_이미지)); final Question 질문 = new Question(경매, 질문자, "질문드립니다."); - 답변 = new Answer("답변드립니다."); + 답변 = new Answer(판매자, "답변드립니다."); 질문.addAnswer(답변); 답변_신고1 = new AnswerReport(이미_신고한_신고자1, 답변, "신고합니다."); 답변_신고2 = new AnswerReport(이미_신고한_신고자2, 답변, "신고합니다."); 답변_신고3 = new AnswerReport(이미_신고한_신고자3, 답변, "신고합니다."); - userRepository.saveAll(List.of(판매자, 질문자, 답변자, 신고자, 이미_신고한_신고자1, 이미_신고한_신고자2, 이미_신고한_신고자3)); + userRepository.save(판매자); + userRepository.save(질문자); + userRepository.save(답변자); + userRepository.save(신고자); + userRepository.save(이미_신고한_신고자1); + userRepository.save(이미_신고한_신고자2); + userRepository.save(이미_신고한_신고자3); + categoryRepository.saveAll(List.of(전자기기_카테고리, 전자기기_서브_노트북_카테고리)); auctionRepository.save(경매); questionRepository.save(질문); answerRepository.save(답변); - answerReportRepository.saveAll(List.of(답변_신고1, 답변_신고2, 답변_신고3)); + answerReportRepository.save(답변_신고1); + answerReportRepository.save(답변_신고2); + answerReportRepository.save(답변_신고3); 답변_신고_요청_dto = new CreateAnswerReportDto(답변.getId(), "신고합니다.", 신고자.getId()); 존재하지_않는_답변_신고_요청_dto = new CreateAnswerReportDto(존재하지_않는_답변_아이디, "신고합니다.", 신고자.getId()); diff --git a/backend/ddang/src/test/java/com/ddang/ddang/report/application/fixture/AuctionReportServiceFixture.java b/backend/ddang/src/test/java/com/ddang/ddang/report/application/fixture/AuctionReportServiceFixture.java index eac55d091..caedb9689 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/report/application/fixture/AuctionReportServiceFixture.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/report/application/fixture/AuctionReportServiceFixture.java @@ -3,7 +3,7 @@ import com.ddang.ddang.auction.domain.Auction; import com.ddang.ddang.auction.domain.BidUnit; import com.ddang.ddang.auction.domain.Price; -import com.ddang.ddang.auction.infrastructure.persistence.JpaAuctionRepository; +import com.ddang.ddang.auction.domain.repository.AuctionRepository; import com.ddang.ddang.category.domain.Category; import com.ddang.ddang.category.infrastructure.persistence.JpaCategoryRepository; import com.ddang.ddang.image.domain.AuctionImage; @@ -12,10 +12,10 @@ import com.ddang.ddang.image.infrastructure.persistence.JpaProfileImageRepository; import com.ddang.ddang.report.application.dto.CreateAuctionReportDto; import com.ddang.ddang.report.domain.AuctionReport; -import com.ddang.ddang.report.infrastructure.persistence.JpaAuctionReportRepository; +import com.ddang.ddang.report.domain.repository.AuctionReportRepository; import com.ddang.ddang.user.domain.Reliability; import com.ddang.ddang.user.domain.User; -import com.ddang.ddang.user.infrastructure.persistence.JpaUserRepository; +import com.ddang.ddang.user.domain.repository.UserRepository; import org.junit.jupiter.api.BeforeEach; import org.springframework.beans.factory.annotation.Autowired; @@ -32,16 +32,16 @@ public class AuctionReportServiceFixture { private JpaProfileImageRepository profileImageRepository; @Autowired - private JpaUserRepository userRepository; + private UserRepository userRepository; @Autowired private JpaAuctionImageRepository auctionImageRepository; @Autowired - private JpaAuctionRepository auctionRepository; + private AuctionRepository auctionRepository; @Autowired - private JpaAuctionReportRepository auctionReportRepository; + private AuctionReportRepository auctionReportRepository; protected User 이미_신고한_신고자1; protected User 이미_신고한_신고자2; @@ -108,14 +108,14 @@ void setUp() { 경매.addAuctionImages(List.of(경매_이미지)); final Auction 삭제된_경매 = Auction.builder() - .seller(판매자) - .title("삭제된 경매 상품") - .description("이것은 삭제된 경매 상품입니다.") - .subCategory(전자기기_서브_노트북_카테고리) - .bidUnit(new BidUnit(1_000)) - .startPrice(new Price(1_000)) - .closingTime(LocalDateTime.now()) - .build(); + .seller(판매자) + .title("삭제된 경매 상품") + .description("이것은 삭제된 경매 상품입니다.") + .subCategory(전자기기_서브_노트북_카테고리) + .bidUnit(new BidUnit(1_000)) + .startPrice(new Price(1_000)) + .closingTime(LocalDateTime.now()) + .build(); 삭제된_경매.addAuctionImages(List.of(경매_이미지)); 삭제된_경매.delete(); @@ -124,13 +124,21 @@ void setUp() { final AuctionReport 경매_신고3 = new AuctionReport(이미_신고한_신고자3, 경매, "신고합니다"); profileImageRepository.save(프로필_이미지); - userRepository.saveAll(List.of(판매자, 새로운_신고자, 이미_신고한_신고자1, 이미_신고한_신고자2, 이미_신고한_신고자3)); + + userRepository.save(판매자); + userRepository.save(새로운_신고자); + userRepository.save(이미_신고한_신고자1); + userRepository.save(이미_신고한_신고자2); + userRepository.save(이미_신고한_신고자3); categoryRepository.saveAll(List.of(전자기기_카테고리, 전자기기_서브_노트북_카테고리)); auctionImageRepository.save(경매_이미지); - auctionRepository.saveAll(List.of(경매, 삭제된_경매)); + auctionRepository.save(경매); + auctionRepository.save(삭제된_경매); - auctionReportRepository.saveAll(List.of(경매_신고1, 경매_신고2, 경매_신고3)); + auctionReportRepository.save(경매_신고1); + auctionReportRepository.save(경매_신고2); + auctionReportRepository.save(경매_신고3); 새로운_경매_신고_요청_dto = new CreateAuctionReportDto( 경매.getId(), diff --git a/backend/ddang/src/test/java/com/ddang/ddang/report/application/fixture/ChatRoomReportServiceFixture.java b/backend/ddang/src/test/java/com/ddang/ddang/report/application/fixture/ChatRoomReportServiceFixture.java index 0f2ac6fda..e6be807e4 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/report/application/fixture/ChatRoomReportServiceFixture.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/report/application/fixture/ChatRoomReportServiceFixture.java @@ -3,21 +3,21 @@ import com.ddang.ddang.auction.domain.Auction; import com.ddang.ddang.auction.domain.BidUnit; import com.ddang.ddang.auction.domain.Price; -import com.ddang.ddang.auction.infrastructure.persistence.JpaAuctionRepository; +import com.ddang.ddang.auction.domain.repository.AuctionRepository; import com.ddang.ddang.category.domain.Category; import com.ddang.ddang.category.infrastructure.persistence.JpaCategoryRepository; import com.ddang.ddang.chat.domain.ChatRoom; -import com.ddang.ddang.chat.infrastructure.persistence.JpaChatRoomRepository; +import com.ddang.ddang.chat.domain.repository.ChatRoomRepository; import com.ddang.ddang.image.domain.AuctionImage; import com.ddang.ddang.image.domain.ProfileImage; import com.ddang.ddang.image.infrastructure.persistence.JpaAuctionImageRepository; import com.ddang.ddang.image.infrastructure.persistence.JpaProfileImageRepository; import com.ddang.ddang.report.application.dto.CreateChatRoomReportDto; import com.ddang.ddang.report.domain.ChatRoomReport; -import com.ddang.ddang.report.infrastructure.persistence.JpaChatRoomReportRepository; +import com.ddang.ddang.report.domain.repository.ChatRoomReportRepository; import com.ddang.ddang.user.domain.Reliability; import com.ddang.ddang.user.domain.User; -import com.ddang.ddang.user.infrastructure.persistence.JpaUserRepository; +import com.ddang.ddang.user.domain.repository.UserRepository; import org.junit.jupiter.api.BeforeEach; import org.springframework.beans.factory.annotation.Autowired; @@ -34,19 +34,19 @@ public class ChatRoomReportServiceFixture { private JpaProfileImageRepository profileImageRepository; @Autowired - private JpaUserRepository userRepository; + private UserRepository userRepository; @Autowired private JpaAuctionImageRepository auctionImageRepository; @Autowired - private JpaAuctionRepository auctionRepository; + private AuctionRepository auctionRepository; @Autowired - private JpaChatRoomReportRepository chatRoomReportRepository; + private ChatRoomReportRepository chatRoomReportRepository; @Autowired - private JpaChatRoomRepository chatRoomRepository; + private ChatRoomRepository chatRoomRepository; protected User 이미_신고한_구매자1; protected User 이미_신고한_구매자2; @@ -143,15 +143,26 @@ void setUp() { final ChatRoomReport 채팅방_신고3 = new ChatRoomReport(이미_신고한_구매자3, 채팅방3, "신고합니다."); profileImageRepository.save(프로필_이미지); - userRepository.saveAll(List.of(판매자겸_아직_신고하지_않은_신고자, 이미_신고한_구매자1, 이미_신고한_구매자2, 이미_신고한_구매자3, 채팅방_참여자가_아닌_사용자)); + + userRepository.save(판매자겸_아직_신고하지_않은_신고자); + userRepository.save(이미_신고한_구매자1); + userRepository.save(이미_신고한_구매자2); + userRepository.save(이미_신고한_구매자3); + userRepository.save(채팅방_참여자가_아닌_사용자); categoryRepository.saveAll(List.of(전자기기_카테고리, 전자기기_서브_노트북_카테고리)); auctionImageRepository.save(경매_이미지); - auctionRepository.saveAll(List.of(경매1, 경매2, 경매3)); + auctionRepository.save(경매1); + auctionRepository.save(경매2); + auctionRepository.save(경매3); - chatRoomRepository.saveAll(List.of(채팅방1, 채팅방2, 채팅방3)); + chatRoomRepository.save(채팅방1); + chatRoomRepository.save(채팅방2); + chatRoomRepository.save(채팅방3); - chatRoomReportRepository.saveAll(List.of(채팅방_신고1, 채팅방_신고2, 채팅방_신고3)); + chatRoomReportRepository.save(채팅방_신고1); + chatRoomReportRepository.save(채팅방_신고2); + chatRoomReportRepository.save(채팅방_신고3); 채팅방_신고_요청_dto = new CreateChatRoomReportDto( 채팅방1.getId(), diff --git a/backend/ddang/src/test/java/com/ddang/ddang/report/application/fixture/QuestionReportServiceFixture.java b/backend/ddang/src/test/java/com/ddang/ddang/report/application/fixture/QuestionReportServiceFixture.java index 88832351d..7e7bb9d8e 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/report/application/fixture/QuestionReportServiceFixture.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/report/application/fixture/QuestionReportServiceFixture.java @@ -3,19 +3,19 @@ import com.ddang.ddang.auction.domain.Auction; import com.ddang.ddang.auction.domain.BidUnit; import com.ddang.ddang.auction.domain.Price; -import com.ddang.ddang.auction.infrastructure.persistence.JpaAuctionRepository; +import com.ddang.ddang.auction.domain.repository.AuctionRepository; import com.ddang.ddang.category.domain.Category; import com.ddang.ddang.category.infrastructure.persistence.JpaCategoryRepository; import com.ddang.ddang.image.domain.AuctionImage; import com.ddang.ddang.image.domain.ProfileImage; import com.ddang.ddang.qna.domain.Question; -import com.ddang.ddang.qna.infrastructure.JpaQuestionRepository; +import com.ddang.ddang.qna.domain.repository.QuestionRepository; import com.ddang.ddang.report.application.dto.CreateQuestionReportDto; import com.ddang.ddang.report.domain.QuestionReport; -import com.ddang.ddang.report.infrastructure.persistence.JpaQuestionReportRepository; +import com.ddang.ddang.report.domain.repository.QuestionReportRepository; import com.ddang.ddang.user.domain.Reliability; import com.ddang.ddang.user.domain.User; -import com.ddang.ddang.user.infrastructure.persistence.JpaUserRepository; +import com.ddang.ddang.user.domain.repository.UserRepository; import org.junit.jupiter.api.BeforeEach; import org.springframework.beans.factory.annotation.Autowired; @@ -29,16 +29,16 @@ public class QuestionReportServiceFixture { private JpaCategoryRepository categoryRepository; @Autowired - private JpaUserRepository userRepository; + private UserRepository userRepository; @Autowired - private JpaAuctionRepository auctionRepository; + private AuctionRepository auctionRepository; @Autowired - private JpaQuestionRepository questionRepository; + private QuestionRepository questionRepository; @Autowired - private JpaQuestionReportRepository questionReportRepository; + private QuestionReportRepository questionReportRepository; protected User 이미_신고한_신고자1; protected User 이미_신고한_신고자2; @@ -116,11 +116,19 @@ void setUp() { 질문_신고2 = new QuestionReport(이미_신고한_신고자2, 질문, "신고합니다."); 질문_신고3 = new QuestionReport(이미_신고한_신고자3, 질문, "신고합니다."); - userRepository.saveAll(List.of(판매자, 질문자, 신고자, 이미_신고한_신고자1, 이미_신고한_신고자2, 이미_신고한_신고자3)); + userRepository.save(판매자); + userRepository.save(질문자); + userRepository.save(신고자); + userRepository.save(이미_신고한_신고자1); + userRepository.save(이미_신고한_신고자2); + userRepository.save(이미_신고한_신고자3); + categoryRepository.saveAll(List.of(전자기기_카테고리, 전자기기_서브_노트북_카테고리)); auctionRepository.save(경매); questionRepository.save(질문); - questionReportRepository.saveAll(List.of(질문_신고1, 질문_신고2, 질문_신고3)); + questionReportRepository.save(질문_신고1); + questionReportRepository.save(질문_신고2); + questionReportRepository.save(질문_신고3); 질문_신고_요청_dto = new CreateQuestionReportDto(질문.getId(), "신고합니다.", 신고자.getId()); 존재하지_않는_질문_신고_요청_dto = new CreateQuestionReportDto(존재하지_않는_질문_아이디, "신고합니다.", 신고자.getId()); diff --git a/backend/ddang/src/test/java/com/ddang/ddang/report/infrastructure/persistence/AnswerReportRepositoryImplTest.java b/backend/ddang/src/test/java/com/ddang/ddang/report/infrastructure/persistence/AnswerReportRepositoryImplTest.java new file mode 100644 index 000000000..ae270e0e6 --- /dev/null +++ b/backend/ddang/src/test/java/com/ddang/ddang/report/infrastructure/persistence/AnswerReportRepositoryImplTest.java @@ -0,0 +1,78 @@ +package com.ddang.ddang.report.infrastructure.persistence; + +import com.ddang.ddang.configuration.JpaConfiguration; +import com.ddang.ddang.configuration.QuerydslConfiguration; +import com.ddang.ddang.report.domain.AnswerReport; +import com.ddang.ddang.report.domain.repository.AnswerReportRepository; +import com.ddang.ddang.report.infrastructure.persistence.fixture.AnswerReportRepositoryImplFixture; +import org.assertj.core.api.SoftAssertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.context.annotation.Import; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +@DataJpaTest +@Import({JpaConfiguration.class, QuerydslConfiguration.class}) +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) +@SuppressWarnings("NonAsciiCharacters") +class AnswerReportRepositoryImplTest extends AnswerReportRepositoryImplFixture { + + AnswerReportRepository answerReportRepository; + + @BeforeEach + void setUp(@Autowired final JpaAnswerReportRepository jpaAnswerReportRepository) { + answerReportRepository = new AnswerReportRepositoryImpl(jpaAnswerReportRepository); + } + + @Test + void 답변을_등록한다() { + // given + final AnswerReport answerReport = new AnswerReport(신고자, 답변, 신고_내용); + + // when + final AnswerReport actual = answerReportRepository.save(answerReport); + + // then + assertThat(actual.getId()).isPositive(); + } + + @Test + void 신고한_답변이라면_참을_반환한다() { + // when + final boolean actual = answerReportRepository.existsByAnswerIdAndReporterId(이미_신고된_답변.getId(), 신고자.getId()); + + // then + assertThat(actual).isTrue(); + } + + @Test + void 신고_전인_답변이라면_거짓을_반환한다() { + // when + final boolean actual = answerReportRepository.existsByAnswerIdAndReporterId(답변.getId(), 신고자.getId()); + + // then + assertThat(actual).isFalse(); + } + + @Test + void 신고된_답변_목록을_조회한다() { + // when + final List actual = answerReportRepository.findAll(); + + // then + SoftAssertions.assertSoftly(softAssertions -> { + softAssertions.assertThat(actual).hasSize(4); + softAssertions.assertThat(actual.get(0)).isEqualTo(답변_신고1); + softAssertions.assertThat(actual.get(1)).isEqualTo(답변_신고2); + softAssertions.assertThat(actual.get(2)).isEqualTo(답변_신고3); + softAssertions.assertThat(actual.get(3)).isEqualTo(답변_신고4); + }); + } +} diff --git a/backend/ddang/src/test/java/com/ddang/ddang/report/infrastructure/persistence/AuctionReportRepositoryImplTest.java b/backend/ddang/src/test/java/com/ddang/ddang/report/infrastructure/persistence/AuctionReportRepositoryImplTest.java new file mode 100644 index 000000000..fd88a30bf --- /dev/null +++ b/backend/ddang/src/test/java/com/ddang/ddang/report/infrastructure/persistence/AuctionReportRepositoryImplTest.java @@ -0,0 +1,80 @@ +package com.ddang.ddang.report.infrastructure.persistence; + +import com.ddang.ddang.configuration.JpaConfiguration; +import com.ddang.ddang.configuration.QuerydslConfiguration; +import com.ddang.ddang.report.domain.AuctionReport; +import com.ddang.ddang.report.domain.repository.AuctionReportRepository; +import com.ddang.ddang.report.infrastructure.persistence.fixture.AuctionReportRepositoryImplFixture; +import org.assertj.core.api.SoftAssertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.context.annotation.Import; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +@DataJpaTest +@Import({JpaConfiguration.class, QuerydslConfiguration.class}) +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) +@SuppressWarnings("NonAsciiCharacters") +class AuctionReportRepositoryImplTest extends AuctionReportRepositoryImplFixture { + + AuctionReportRepository auctionReportRepository; + + @BeforeEach + void setUp(@Autowired final JpaAuctionReportRepository jpaAuctionReportRepository) { + auctionReportRepository = new AuctionReportRepositoryImpl(jpaAuctionReportRepository); + } + + @Test + void 경매_신고를_저장한다() { + // given + final AuctionReport auctionReport = new AuctionReport(판매자, 경매, "신고합니다"); + + // when + final AuctionReport actual = auctionReportRepository.save(auctionReport); + + // then + assertThat(actual.getId()).isPositive(); + } + + @Test + void 특정_경매_아이디와_신고자_아이디가_동일한_레코드가_존재하면_참을_반환한다() { + // when + final boolean actual = auctionReportRepository.existsByAuctionIdAndReporterId(경매.getId(), 신고자1.getId()); + + // then + assertThat(actual).isTrue(); + } + + @Test + void 특정_경매_아이디와_신고자_아이디가_동일한_레코드가_존재하지_않는다면_거짓을_반환한다() { + // when + final boolean actual = auctionReportRepository.existsByAuctionIdAndReporterId( + 존재하지_않는_경매_아이디, + 존재하지_않는_사용자_아이디 + ); + + // then + assertThat(actual).isFalse(); + } + + @Test + void 전체_경매_신고_목록을_조회한다() { + // when + final List actual = auctionReportRepository.findAll(); + + // then + SoftAssertions.assertSoftly(softAssertions -> { + softAssertions.assertThat(actual).hasSize(3); + softAssertions.assertThat(actual.get(0)).isEqualTo(경매_신고1); + softAssertions.assertThat(actual.get(1)).isEqualTo(경매_신고2); + softAssertions.assertThat(actual.get(2)).isEqualTo(경매_신고3); + }); + } +} diff --git a/backend/ddang/src/test/java/com/ddang/ddang/report/infrastructure/persistence/ChatRoomReportRepositoryImplTest.java b/backend/ddang/src/test/java/com/ddang/ddang/report/infrastructure/persistence/ChatRoomReportRepositoryImplTest.java new file mode 100644 index 000000000..8b76d3c3d --- /dev/null +++ b/backend/ddang/src/test/java/com/ddang/ddang/report/infrastructure/persistence/ChatRoomReportRepositoryImplTest.java @@ -0,0 +1,80 @@ +package com.ddang.ddang.report.infrastructure.persistence; + +import com.ddang.ddang.configuration.JpaConfiguration; +import com.ddang.ddang.configuration.QuerydslConfiguration; +import com.ddang.ddang.report.domain.ChatRoomReport; +import com.ddang.ddang.report.domain.repository.ChatRoomReportRepository; +import com.ddang.ddang.report.infrastructure.persistence.fixture.ChatRoomReportRepositoryImplFixture; +import org.assertj.core.api.SoftAssertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.context.annotation.Import; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +@DataJpaTest +@Import({JpaConfiguration.class, QuerydslConfiguration.class}) +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) +@SuppressWarnings("NonAsciiCharacters") +class ChatRoomReportRepositoryImplTest extends ChatRoomReportRepositoryImplFixture { + + ChatRoomReportRepository chatRoomReportRepository; + + @BeforeEach + void setUp(@Autowired final JpaChatRoomReportRepository jpaChatRoomReportRepository) { + chatRoomReportRepository = new ChatRoomReportRepositoryImpl(jpaChatRoomReportRepository); + } + + @Test + void 채팅방_신고를_저장한다() { + // given + final ChatRoomReport chatRoomReport = new ChatRoomReport(구매자1, 채팅방1, "신고합니다."); + + // when + final ChatRoomReport actual = chatRoomReportRepository.save(chatRoomReport); + + // then + assertThat(actual.getId()).isPositive(); + } + + @Test + void 특정_채팅방_아이디와_신고자_아이디가_동일한_레코드가_존재한다면_참을_반환한다() { + // when + final boolean actual = chatRoomReportRepository.existsByChatRoomIdAndReporterId(채팅방1.getId(), 구매자1겸_신고자.getId()); + + // then + assertThat(actual).isTrue(); + } + + @Test + void 특정_채팅방_아이디와_신고자_아이디가_동일한_레코드가_존재하지_않는다면_거짓을_반환한다() { + // when + final boolean actual = chatRoomReportRepository.existsByChatRoomIdAndReporterId( + 존재하지_않는_채팅방_아이디, + 존재하지_않는_사용자_아이디 + ); + + // then + assertThat(actual).isFalse(); + } + + @Test + void 전체_채팅방_신고_목록을_조회한다() { + // when + final List actual = chatRoomReportRepository.findAll(); + + // then + SoftAssertions.assertSoftly(softAssertions -> { + softAssertions.assertThat(actual).hasSize(3); + softAssertions.assertThat(actual.get(0)).isEqualTo(채팅방_신고1); + softAssertions.assertThat(actual.get(1)).isEqualTo(채팅방_신고2); + softAssertions.assertThat(actual.get(2)).isEqualTo(채팅방_신고3); + }); + } +} diff --git a/backend/ddang/src/test/java/com/ddang/ddang/report/infrastructure/persistence/JpaAnswerReportRepositoryTest.java b/backend/ddang/src/test/java/com/ddang/ddang/report/infrastructure/persistence/JpaAnswerReportRepositoryTest.java index 4e1a9c379..1606e77e7 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/report/infrastructure/persistence/JpaAnswerReportRepositoryTest.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/report/infrastructure/persistence/JpaAnswerReportRepositoryTest.java @@ -58,7 +58,7 @@ class JpaAnswerReportRepositoryTest extends JpaAnswerReportRepositoryFixture { @Test void 신고된_답변_목록을_조회한다() { // when - final List actual = answerReportRepository.findAllByOrderByIdAsc(); + final List actual = answerReportRepository.findAll(); // then SoftAssertions.assertSoftly(softAssertions -> { diff --git a/backend/ddang/src/test/java/com/ddang/ddang/report/infrastructure/persistence/JpaAuctionReportRepositoryTest.java b/backend/ddang/src/test/java/com/ddang/ddang/report/infrastructure/persistence/JpaAuctionReportRepositoryTest.java index 20dd7cfa4..48aff7243 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/report/infrastructure/persistence/JpaAuctionReportRepositoryTest.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/report/infrastructure/persistence/JpaAuctionReportRepositoryTest.java @@ -4,8 +4,6 @@ import com.ddang.ddang.configuration.QuerydslConfiguration; import com.ddang.ddang.report.domain.AuctionReport; import com.ddang.ddang.report.infrastructure.persistence.fixture.JpaAuctionReportRepositoryFixture; -import jakarta.persistence.EntityManager; -import jakarta.persistence.PersistenceContext; import org.assertj.core.api.SoftAssertions; import org.junit.jupiter.api.DisplayNameGeneration; import org.junit.jupiter.api.DisplayNameGenerator; @@ -24,9 +22,6 @@ @SuppressWarnings("NonAsciiCharacters") class JpaAuctionReportRepositoryTest extends JpaAuctionReportRepositoryFixture { - @PersistenceContext - EntityManager em; - @Autowired JpaAuctionReportRepository auctionReportRepository; @@ -39,9 +34,6 @@ class JpaAuctionReportRepositoryTest extends JpaAuctionReportRepositoryFixture { final AuctionReport actual = auctionReportRepository.save(auctionReport); // then - em.flush(); - em.clear(); - assertThat(actual.getId()).isPositive(); } @@ -69,7 +61,7 @@ class JpaAuctionReportRepositoryTest extends JpaAuctionReportRepositoryFixture { @Test void 전체_경매_신고_목록을_조회한다() { // when - final List actual = auctionReportRepository.findAllByOrderByIdAsc(); + final List actual = auctionReportRepository.findAll(); // then SoftAssertions.assertSoftly(softAssertions -> { diff --git a/backend/ddang/src/test/java/com/ddang/ddang/report/infrastructure/persistence/JpaChatRoomReportRepositoryTest.java b/backend/ddang/src/test/java/com/ddang/ddang/report/infrastructure/persistence/JpaChatRoomReportRepositoryTest.java index 4bd9d2bf1..8030b4a76 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/report/infrastructure/persistence/JpaChatRoomReportRepositoryTest.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/report/infrastructure/persistence/JpaChatRoomReportRepositoryTest.java @@ -4,8 +4,6 @@ import com.ddang.ddang.configuration.QuerydslConfiguration; import com.ddang.ddang.report.domain.ChatRoomReport; import com.ddang.ddang.report.infrastructure.persistence.fixture.JpaChatRoomReportRepositoryFixture; -import jakarta.persistence.EntityManager; -import jakarta.persistence.PersistenceContext; import org.assertj.core.api.SoftAssertions; import org.junit.jupiter.api.DisplayNameGeneration; import org.junit.jupiter.api.DisplayNameGenerator; @@ -24,23 +22,18 @@ @SuppressWarnings("NonAsciiCharacters") class JpaChatRoomReportRepositoryTest extends JpaChatRoomReportRepositoryFixture { - @PersistenceContext - EntityManager em; - @Autowired JpaChatRoomReportRepository chatRoomReportRepository; @Test void 채팅방_신고를_저장한다() { + // given final ChatRoomReport chatRoomReport = new ChatRoomReport(구매자1, 채팅방1, "신고합니다."); // when final ChatRoomReport actual = chatRoomReportRepository.save(chatRoomReport); // then - em.flush(); - em.clear(); - assertThat(actual.getId()).isPositive(); } @@ -68,7 +61,7 @@ class JpaChatRoomReportRepositoryTest extends JpaChatRoomReportRepositoryFixture @Test void 전체_채팅방_신고_목록을_조회한다() { // when - final List actual = chatRoomReportRepository.findAllByOrderByIdAsc(); + final List actual = chatRoomReportRepository.findAll(); // then SoftAssertions.assertSoftly(softAssertions -> { diff --git a/backend/ddang/src/test/java/com/ddang/ddang/report/infrastructure/persistence/JpaQuestionReportRepositoryTest.java b/backend/ddang/src/test/java/com/ddang/ddang/report/infrastructure/persistence/JpaQuestionReportRepositoryImplTest.java similarity index 94% rename from backend/ddang/src/test/java/com/ddang/ddang/report/infrastructure/persistence/JpaQuestionReportRepositoryTest.java rename to backend/ddang/src/test/java/com/ddang/ddang/report/infrastructure/persistence/JpaQuestionReportRepositoryImplTest.java index 7c85405e7..c26823861 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/report/infrastructure/persistence/JpaQuestionReportRepositoryTest.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/report/infrastructure/persistence/JpaQuestionReportRepositoryImplTest.java @@ -3,7 +3,7 @@ import com.ddang.ddang.configuration.JpaConfiguration; import com.ddang.ddang.configuration.QuerydslConfiguration; import com.ddang.ddang.report.domain.QuestionReport; -import com.ddang.ddang.report.infrastructure.persistence.fixture.JpaQuestionReportRepositoryFixture; +import com.ddang.ddang.report.infrastructure.persistence.fixture.JpaQuestionReportRepositoryImplFixture; import org.assertj.core.api.SoftAssertions; import org.junit.jupiter.api.DisplayNameGeneration; import org.junit.jupiter.api.DisplayNameGenerator; @@ -20,7 +20,7 @@ @Import({JpaConfiguration.class, QuerydslConfiguration.class}) @DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) @SuppressWarnings("NonAsciiCharacters") -class JpaQuestionReportRepositoryTest extends JpaQuestionReportRepositoryFixture { +class JpaQuestionReportRepositoryImplTest extends JpaQuestionReportRepositoryImplFixture { @Autowired JpaQuestionReportRepository questionReportRepository; @@ -58,7 +58,7 @@ class JpaQuestionReportRepositoryTest extends JpaQuestionReportRepositoryFixture @Test void 신고된_질문_목록을_조회한다() { // when - final List actual = questionReportRepository.findAllByOrderByIdAsc(); + final List actual = questionReportRepository.findAll(); // then SoftAssertions.assertSoftly(softAssertions -> { diff --git a/backend/ddang/src/test/java/com/ddang/ddang/report/infrastructure/persistence/QuestionReportRepositoryImplTest.java b/backend/ddang/src/test/java/com/ddang/ddang/report/infrastructure/persistence/QuestionReportRepositoryImplTest.java new file mode 100644 index 000000000..92697fa61 --- /dev/null +++ b/backend/ddang/src/test/java/com/ddang/ddang/report/infrastructure/persistence/QuestionReportRepositoryImplTest.java @@ -0,0 +1,78 @@ +package com.ddang.ddang.report.infrastructure.persistence; + +import com.ddang.ddang.configuration.JpaConfiguration; +import com.ddang.ddang.configuration.QuerydslConfiguration; +import com.ddang.ddang.report.domain.QuestionReport; +import com.ddang.ddang.report.domain.repository.QuestionReportRepository; +import com.ddang.ddang.report.infrastructure.persistence.fixture.QuestionReportRepositoryImplFixture; +import org.assertj.core.api.SoftAssertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.context.annotation.Import; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +@DataJpaTest +@Import({JpaConfiguration.class, QuerydslConfiguration.class}) +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) +@SuppressWarnings("NonAsciiCharacters") +class QuestionReportRepositoryImplTest extends QuestionReportRepositoryImplFixture { + + QuestionReportRepository questionReportRepository; + + @BeforeEach + void setUp(@Autowired final JpaQuestionReportRepository jpaQuestionReportRepository) { + questionReportRepository = new QuestionReportRepositoryImpl(jpaQuestionReportRepository); + } + + @Test + void 질문_신고를_등록한다() { + // given + final QuestionReport questionReport = new QuestionReport(신고자, 질문, 신고_내용); + + // when + final QuestionReport actual = questionReportRepository.save(questionReport); + + // then + assertThat(actual.getId()).isPositive(); + } + + @Test + void 신고한_질문이라면_참을_반환한다() { + // when + final boolean actual = questionReportRepository.existsByQuestionIdAndReporterId(이미_신고한_질문.getId(), 신고자.getId()); + + // then + assertThat(actual).isTrue(); + } + + @Test + void 신고_전인_질문이라면_거짓을_반환한다() { + // when + final boolean actual = questionReportRepository.existsByQuestionIdAndReporterId(질문.getId(), 신고자.getId()); + + // then + assertThat(actual).isFalse(); + } + + @Test + void 신고된_질문_목록을_조회한다() { + // when + final List actual = questionReportRepository.findAll(); + + // then + SoftAssertions.assertSoftly(softAssertions -> { + softAssertions.assertThat(actual).hasSize(4); + softAssertions.assertThat(actual.get(0)).isEqualTo(질문_신고1); + softAssertions.assertThat(actual.get(1)).isEqualTo(질문_신고2); + softAssertions.assertThat(actual.get(2)).isEqualTo(질문_신고3); + softAssertions.assertThat(actual.get(3)).isEqualTo(질문_신고4); + }); + } +} diff --git a/backend/ddang/src/test/java/com/ddang/ddang/report/infrastructure/persistence/fixture/AnswerReportRepositoryImplFixture.java b/backend/ddang/src/test/java/com/ddang/ddang/report/infrastructure/persistence/fixture/AnswerReportRepositoryImplFixture.java new file mode 100644 index 000000000..bd09d169b --- /dev/null +++ b/backend/ddang/src/test/java/com/ddang/ddang/report/infrastructure/persistence/fixture/AnswerReportRepositoryImplFixture.java @@ -0,0 +1,156 @@ +package com.ddang.ddang.report.infrastructure.persistence.fixture; + +import com.ddang.ddang.auction.domain.Auction; +import com.ddang.ddang.auction.domain.BidUnit; +import com.ddang.ddang.auction.domain.Price; +import com.ddang.ddang.auction.domain.repository.AuctionRepository; +import com.ddang.ddang.auction.infrastructure.persistence.AuctionRepositoryImpl; +import com.ddang.ddang.auction.infrastructure.persistence.JpaAuctionRepository; +import com.ddang.ddang.auction.infrastructure.persistence.QuerydslAuctionRepository; +import com.ddang.ddang.category.domain.Category; +import com.ddang.ddang.category.infrastructure.persistence.JpaCategoryRepository; +import com.ddang.ddang.image.domain.AuctionImage; +import com.ddang.ddang.image.domain.ProfileImage; +import com.ddang.ddang.qna.domain.Answer; +import com.ddang.ddang.qna.domain.Question; +import com.ddang.ddang.qna.infrastructure.JpaAnswerRepository; +import com.ddang.ddang.qna.infrastructure.JpaQuestionRepository; +import com.ddang.ddang.report.domain.AnswerReport; +import com.ddang.ddang.report.domain.repository.AnswerReportRepository; +import com.ddang.ddang.report.infrastructure.persistence.AnswerReportRepositoryImpl; +import com.ddang.ddang.report.infrastructure.persistence.JpaAnswerReportRepository; +import com.ddang.ddang.user.domain.Reliability; +import com.ddang.ddang.user.domain.User; +import com.ddang.ddang.user.domain.repository.UserRepository; +import com.ddang.ddang.user.infrastructure.persistence.JpaUserRepository; +import com.ddang.ddang.user.infrastructure.persistence.UserRepositoryImpl; +import com.querydsl.jpa.impl.JPAQueryFactory; +import org.junit.jupiter.api.BeforeEach; +import org.springframework.beans.factory.annotation.Autowired; + +import java.time.LocalDateTime; +import java.util.List; + +@SuppressWarnings("NonAsciiCharacters") +public class AnswerReportRepositoryImplFixture { + + @Autowired + private JpaCategoryRepository categoryRepository; + + @Autowired + private JpaQuestionRepository questionRepository; + + @Autowired + private JpaAnswerRepository answerRepository; + + private UserRepository userRepository; + + private AuctionRepository auctionRepository; + + private AnswerReportRepository answerReportRepository; + + protected User 신고자; + protected Answer 답변; + protected String 신고_내용 = "신고합니다."; + protected Answer 이미_신고된_답변; + protected AnswerReport 답변_신고1; + protected AnswerReport 답변_신고2; + protected AnswerReport 답변_신고3; + protected AnswerReport 답변_신고4; + + @BeforeEach + void setUpFixture( + @Autowired final JpaUserRepository jpaUserRepository, + @Autowired final JpaAuctionRepository jpaAuctionRepository, + @Autowired final JPAQueryFactory queryFactory, + @Autowired final JpaAnswerReportRepository jpaAnswerReportRepository + ) { + userRepository = new UserRepositoryImpl(jpaUserRepository); + auctionRepository = new AuctionRepositoryImpl(jpaAuctionRepository, new QuerydslAuctionRepository(queryFactory)); + answerReportRepository = new AnswerReportRepositoryImpl(jpaAnswerReportRepository); + + + final ProfileImage 프로필_이미지 = new ProfileImage("프로필.jpg", "프로필.jpg"); + final User 판매자 = User.builder() + .name("판매자") + .profileImage(프로필_이미지) + .reliability(new Reliability(4.7d)) + .oauthId("12345") + .build(); + final User 질문자 = User.builder() + .name("질문자") + .profileImage(프로필_이미지) + .reliability(new Reliability(4.7d)) + .oauthId("12346") + .build(); + 신고자 = User.builder() + .name("신고자") + .profileImage(프로필_이미지) + .reliability(new Reliability(4.7d)) + .oauthId("12347") + .build(); + final User 신고자2 = User.builder() + .name("신고자2") + .profileImage(프로필_이미지) + .reliability(new Reliability(4.7d)) + .oauthId("12348") + .build(); + final User 신고자3 = User.builder() + .name("신고자3") + .profileImage(프로필_이미지) + .reliability(new Reliability(4.7d)) + .oauthId("12349") + .build(); + final User 신고자4 = User.builder() + .name("신고자4") + .profileImage(프로필_이미지) + .reliability(new Reliability(4.7d)) + .oauthId("12350") + .build(); + + final Category 전자기기_카테고리 = new Category("전자기기"); + final Category 전자기기_서브_노트북_카테고리 = new Category("노트북 카테고리"); + 전자기기_카테고리.addSubCategory(전자기기_서브_노트북_카테고리); + final AuctionImage 경매_이미지 = new AuctionImage("경매이미지.jpg", "경매이미지.jpg"); + final Auction 경매 = Auction.builder() + .seller(판매자) + .title("경매 상품") + .description("이것은 경매 상품입니다.") + .subCategory(전자기기_서브_노트북_카테고리) + .bidUnit(new BidUnit(1_000)) + .startPrice(new Price(1_000)) + .closingTime(LocalDateTime.now()) + .build(); + 경매.addAuctionImages(List.of(경매_이미지)); + + final Question 질문1 = new Question(경매, 질문자, "질문드립니다."); + final Question 질문2 = new Question(경매, 질문자, "질문드립니다."); + 답변 = new Answer(판매자, "답변드립니다."); + 이미_신고된_답변 = new Answer(판매자, "답변드립니다."); + 질문1.addAnswer(답변); + 질문2.addAnswer(이미_신고된_답변); + + 답변_신고1 = new AnswerReport(신고자, 이미_신고된_답변, "신고합니다."); + 답변_신고2 = new AnswerReport(신고자2, 이미_신고된_답변, "신고합니다."); + 답변_신고3 = new AnswerReport(신고자3, 이미_신고된_답변, "신고합니다."); + 답변_신고4 = new AnswerReport(신고자4, 이미_신고된_답변, "신고합니다."); + + userRepository.save(판매자); + userRepository.save(질문자); + userRepository.save(신고자); + userRepository.save(신고자2); + userRepository.save(신고자3); + userRepository.save(신고자4); + + + categoryRepository.saveAll(List.of(전자기기_카테고리, 전자기기_서브_노트북_카테고리)); + auctionRepository.save(경매); + + questionRepository.saveAll(List.of(질문1, 질문2)); + answerRepository.saveAll(List.of(답변, 이미_신고된_답변)); + answerReportRepository.save(답변_신고1); + answerReportRepository.save(답변_신고2); + answerReportRepository.save(답변_신고3); + answerReportRepository.save(답변_신고4); + } +} diff --git a/backend/ddang/src/test/java/com/ddang/ddang/report/infrastructure/persistence/fixture/AuctionReportRepositoryImplFixture.java b/backend/ddang/src/test/java/com/ddang/ddang/report/infrastructure/persistence/fixture/AuctionReportRepositoryImplFixture.java new file mode 100644 index 000000000..89b9153b6 --- /dev/null +++ b/backend/ddang/src/test/java/com/ddang/ddang/report/infrastructure/persistence/fixture/AuctionReportRepositoryImplFixture.java @@ -0,0 +1,130 @@ +package com.ddang.ddang.report.infrastructure.persistence.fixture; + +import com.ddang.ddang.auction.domain.Auction; +import com.ddang.ddang.auction.domain.BidUnit; +import com.ddang.ddang.auction.domain.Price; +import com.ddang.ddang.auction.domain.repository.AuctionRepository; +import com.ddang.ddang.auction.infrastructure.persistence.AuctionRepositoryImpl; +import com.ddang.ddang.auction.infrastructure.persistence.JpaAuctionRepository; +import com.ddang.ddang.auction.infrastructure.persistence.QuerydslAuctionRepository; +import com.ddang.ddang.category.domain.Category; +import com.ddang.ddang.category.infrastructure.persistence.JpaCategoryRepository; +import com.ddang.ddang.image.domain.AuctionImage; +import com.ddang.ddang.image.domain.ProfileImage; +import com.ddang.ddang.image.infrastructure.persistence.JpaAuctionImageRepository; +import com.ddang.ddang.image.infrastructure.persistence.JpaProfileImageRepository; +import com.ddang.ddang.report.domain.AuctionReport; +import com.ddang.ddang.report.domain.repository.AuctionReportRepository; +import com.ddang.ddang.report.infrastructure.persistence.AuctionReportRepositoryImpl; +import com.ddang.ddang.report.infrastructure.persistence.JpaAuctionReportRepository; +import com.ddang.ddang.user.domain.Reliability; +import com.ddang.ddang.user.domain.User; +import com.ddang.ddang.user.domain.repository.UserRepository; +import com.ddang.ddang.user.infrastructure.persistence.JpaUserRepository; +import com.ddang.ddang.user.infrastructure.persistence.UserRepositoryImpl; +import com.querydsl.jpa.impl.JPAQueryFactory; +import org.junit.jupiter.api.BeforeEach; +import org.springframework.beans.factory.annotation.Autowired; + +import java.time.LocalDateTime; +import java.util.List; + +@SuppressWarnings("NonAsciiCharacters") +public class AuctionReportRepositoryImplFixture { + + @Autowired + private JpaCategoryRepository categoryRepository; + + @Autowired + private JpaProfileImageRepository profileImageRepository; + + @Autowired + private JpaAuctionImageRepository auctionImageRepository; + + private UserRepository userRepository; + + private AuctionRepository auctionRepository; + + private AuctionReportRepository auctionReportRepository; + + protected Long 존재하지_않는_경매_아이디 = -9999L; + protected Long 존재하지_않는_사용자_아이디 = -9999L; + protected User 판매자; + protected User 신고자1; + protected Auction 경매; + protected AuctionReport 경매_신고1; + protected AuctionReport 경매_신고2; + protected AuctionReport 경매_신고3; + + @BeforeEach + void setUpFixture( + @Autowired final JPAQueryFactory jpaQueryFactory, + @Autowired final JpaUserRepository jpaUserRepository, + @Autowired final JpaAuctionRepository jpaAuctionRepository, + @Autowired final JpaAuctionReportRepository jpaAuctionReportRepository + ) { + userRepository = new UserRepositoryImpl(jpaUserRepository); + auctionRepository = new AuctionRepositoryImpl(jpaAuctionRepository, new QuerydslAuctionRepository(jpaQueryFactory)); + auctionReportRepository = new AuctionReportRepositoryImpl(jpaAuctionReportRepository); + + final ProfileImage 프로필_이미지 = new ProfileImage("프로필.jpg", "프로필.jpg"); + 판매자 = User.builder() + .name("판매자") + .profileImage(프로필_이미지) + .reliability(new Reliability(4.7d)) + .oauthId("12345") + .build(); + 신고자1 = User.builder() + .name("신고자1") + .profileImage(프로필_이미지) + .reliability(new Reliability(4.7d)) + .oauthId("12346") + .build(); + final User 신고자2 = User.builder() + .name("신고자2") + .profileImage(프로필_이미지) + .reliability(new Reliability(4.7d)) + .oauthId("12347") + .build(); + final User 신고자3 = User.builder() + .name("신고자3") + .profileImage(프로필_이미지) + .reliability(new Reliability(4.7d)) + .oauthId("12348") + .build(); + + final Category 전자기기_카테고리 = new Category("전자기기"); + final Category 전자기기_서브_노트북_카테고리 = new Category("노트북 카테고리"); + 전자기기_카테고리.addSubCategory(전자기기_서브_노트북_카테고리); + final AuctionImage 경매_이미지 = new AuctionImage("경매이미지.jpg", "경매이미지.jpg"); + 경매 = Auction.builder() + .seller(판매자) + .title("경매 상품") + .description("이것은 경매 상품입니다.") + .subCategory(전자기기_서브_노트북_카테고리) + .bidUnit(new BidUnit(1_000)) + .startPrice(new Price(1_000)) + .closingTime(LocalDateTime.now()) + .build(); + 경매.addAuctionImages(List.of(경매_이미지)); + + 경매_신고1 = new AuctionReport(신고자1, 경매, "신고합니다"); + 경매_신고2 = new AuctionReport(신고자2, 경매, "신고합니다"); + 경매_신고3 = new AuctionReport(신고자3, 경매, "신고합니다"); + + + profileImageRepository.save(프로필_이미지); + userRepository.save(판매자); + userRepository.save(신고자1); + userRepository.save(신고자2); + userRepository.save(신고자3); + + categoryRepository.saveAll(List.of(전자기기_카테고리, 전자기기_서브_노트북_카테고리)); + auctionImageRepository.save(경매_이미지); + auctionRepository.save(경매); + + auctionReportRepository.save(경매_신고1); + auctionReportRepository.save(경매_신고2); + auctionReportRepository.save(경매_신고3); + } +} diff --git a/backend/ddang/src/test/java/com/ddang/ddang/report/infrastructure/persistence/fixture/ChatRoomReportRepositoryImplFixture.java b/backend/ddang/src/test/java/com/ddang/ddang/report/infrastructure/persistence/fixture/ChatRoomReportRepositoryImplFixture.java new file mode 100644 index 000000000..c86fcc029 --- /dev/null +++ b/backend/ddang/src/test/java/com/ddang/ddang/report/infrastructure/persistence/fixture/ChatRoomReportRepositoryImplFixture.java @@ -0,0 +1,168 @@ +package com.ddang.ddang.report.infrastructure.persistence.fixture; + +import com.ddang.ddang.auction.domain.Auction; +import com.ddang.ddang.auction.domain.BidUnit; +import com.ddang.ddang.auction.domain.Price; +import com.ddang.ddang.auction.domain.repository.AuctionRepository; +import com.ddang.ddang.auction.infrastructure.persistence.AuctionRepositoryImpl; +import com.ddang.ddang.auction.infrastructure.persistence.JpaAuctionRepository; +import com.ddang.ddang.auction.infrastructure.persistence.QuerydslAuctionRepository; +import com.ddang.ddang.category.domain.Category; +import com.ddang.ddang.category.infrastructure.persistence.JpaCategoryRepository; +import com.ddang.ddang.chat.domain.ChatRoom; +import com.ddang.ddang.chat.domain.repository.ChatRoomRepository; +import com.ddang.ddang.chat.infrastructure.persistence.ChatRoomRepositoryImpl; +import com.ddang.ddang.chat.infrastructure.persistence.JpaChatRoomRepository; +import com.ddang.ddang.image.domain.AuctionImage; +import com.ddang.ddang.image.domain.ProfileImage; +import com.ddang.ddang.image.infrastructure.persistence.JpaAuctionImageRepository; +import com.ddang.ddang.image.infrastructure.persistence.JpaProfileImageRepository; +import com.ddang.ddang.report.domain.ChatRoomReport; +import com.ddang.ddang.report.domain.repository.ChatRoomReportRepository; +import com.ddang.ddang.report.infrastructure.persistence.ChatRoomReportRepositoryImpl; +import com.ddang.ddang.report.infrastructure.persistence.JpaChatRoomReportRepository; +import com.ddang.ddang.user.domain.Reliability; +import com.ddang.ddang.user.domain.User; +import com.ddang.ddang.user.domain.repository.UserRepository; +import com.ddang.ddang.user.infrastructure.persistence.JpaUserRepository; +import com.ddang.ddang.user.infrastructure.persistence.UserRepositoryImpl; +import com.querydsl.jpa.impl.JPAQueryFactory; +import org.junit.jupiter.api.BeforeEach; +import org.springframework.beans.factory.annotation.Autowired; + +import java.time.LocalDateTime; +import java.util.List; + +@SuppressWarnings("NonAsciiCharacters") +public class ChatRoomReportRepositoryImplFixture { + + @Autowired + private JpaCategoryRepository categoryRepository; + + @Autowired + private JpaProfileImageRepository profileImageRepository; + + @Autowired + private JpaAuctionImageRepository auctionImageRepository; + + private UserRepository userRepository; + + private AuctionRepository auctionRepository; + + private ChatRoomRepository chatRoomRepository; + + private ChatRoomReportRepository chatRoomReportRepository; + + protected Long 존재하지_않는_채팅방_아이디 = -9999L; + protected Long 존재하지_않는_사용자_아이디 = -9999L; + protected User 구매자1; + protected User 구매자1겸_신고자; + protected ChatRoom 채팅방1; + protected ChatRoomReport 채팅방_신고1; + protected ChatRoomReport 채팅방_신고2; + protected ChatRoomReport 채팅방_신고3; + + @BeforeEach + void setUpFixture( + @Autowired final JpaUserRepository jpaUserRepository, + @Autowired final JpaAuctionRepository jpaAuctionRepository, + @Autowired final JPAQueryFactory queryFactory, + @Autowired final JpaChatRoomRepository jpaChatRoomRepository, + @Autowired final JpaChatRoomReportRepository jpaChatRoomReportRepository + ) { + userRepository = new UserRepositoryImpl(jpaUserRepository); + auctionRepository = new AuctionRepositoryImpl(jpaAuctionRepository, new QuerydslAuctionRepository(queryFactory)); + chatRoomRepository = new ChatRoomRepositoryImpl(jpaChatRoomRepository); + chatRoomReportRepository = new ChatRoomReportRepositoryImpl(jpaChatRoomReportRepository); + + final ProfileImage 프로필_이미지 = new ProfileImage("프로필.jpg", "프로필.jpg"); + final User 판매자 = User.builder() + .name("판매자") + .profileImage(프로필_이미지) + .reliability(new Reliability(4.7d)) + .oauthId("12345") + .build(); + 구매자1 = User.builder() + .name("구매자1") + .profileImage(프로필_이미지) + .reliability(new Reliability(4.7d)) + .oauthId("12346") + .build(); + final User 구매자2겸_신고자 = User.builder() + .name("구매자2") + .profileImage(프로필_이미지) + .reliability(new Reliability(4.7d)) + .oauthId("12347") + .build(); + final User 구매자3겸_신고자 = User.builder() + .name("구매자3") + .profileImage(프로필_이미지) + .reliability(new Reliability(4.7d)) + .oauthId("12348") + .build(); + 구매자1겸_신고자 = 구매자1; + + final Category 전자기기_카테고리 = new Category("전자기기"); + final Category 전자기기_서브_노트북_카테고리 = new Category("노트북 카테고리"); + 전자기기_카테고리.addSubCategory(전자기기_서브_노트북_카테고리); + final AuctionImage 경매_이미지 = new AuctionImage("경매이미지.jpg", "경매이미지.jpg"); + final Auction 경매1 = Auction.builder() + .seller(판매자) + .title("경매 상품") + .description("이것은 경매 상품입니다.") + .subCategory(전자기기_서브_노트북_카테고리) + .bidUnit(new BidUnit(1_000)) + .startPrice(new Price(1_000)) + .closingTime(LocalDateTime.now()) + .build(); + 경매1.addAuctionImages(List.of(경매_이미지)); + final Auction 경매2 = Auction.builder() + .seller(판매자) + .title("경매 상품") + .description("이것은 경매 상품입니다.") + .subCategory(전자기기_서브_노트북_카테고리) + .bidUnit(new BidUnit(1_000)) + .startPrice(new Price(1_000)) + .closingTime(LocalDateTime.now()) + .build(); + 경매2.addAuctionImages(List.of(경매_이미지)); + final Auction 경매3 = Auction.builder() + .seller(판매자) + .title("경매 상품") + .description("이것은 경매 상품입니다.") + .subCategory(전자기기_서브_노트북_카테고리) + .bidUnit(new BidUnit(1_000)) + .startPrice(new Price(1_000)) + .closingTime(LocalDateTime.now()) + .build(); + 경매3.addAuctionImages(List.of(경매_이미지)); + + 채팅방1 = new ChatRoom(경매1, 구매자1); + final ChatRoom 채팅방2 = new ChatRoom(경매2, 구매자2겸_신고자); + final ChatRoom 채팅방3 = new ChatRoom(경매3, 구매자3겸_신고자); + + 채팅방_신고1 = new ChatRoomReport(구매자1겸_신고자, 채팅방1, "신고합니다."); + 채팅방_신고2 = new ChatRoomReport(구매자2겸_신고자, 채팅방2, "신고합니다."); + 채팅방_신고3 = new ChatRoomReport(구매자3겸_신고자, 채팅방3, "신고합니다."); + + profileImageRepository.save(프로필_이미지); + userRepository.save(판매자); + userRepository.save(구매자1); + userRepository.save(구매자2겸_신고자); + userRepository.save(구매자3겸_신고자); + + categoryRepository.saveAll(List.of(전자기기_카테고리, 전자기기_서브_노트북_카테고리)); + auctionImageRepository.save(경매_이미지); + auctionRepository.save(경매1); + auctionRepository.save(경매2); + auctionRepository.save(경매3); + + chatRoomRepository.save(채팅방1); + chatRoomRepository.save(채팅방2); + chatRoomRepository.save(채팅방3); + + chatRoomReportRepository.save(채팅방_신고1); + chatRoomReportRepository.save(채팅방_신고2); + chatRoomReportRepository.save(채팅방_신고3); + } +} diff --git a/backend/ddang/src/test/java/com/ddang/ddang/report/infrastructure/persistence/fixture/JpaAnswerReportRepositoryFixture.java b/backend/ddang/src/test/java/com/ddang/ddang/report/infrastructure/persistence/fixture/JpaAnswerReportRepositoryFixture.java index dd47fd348..d3f880bd1 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/report/infrastructure/persistence/fixture/JpaAnswerReportRepositoryFixture.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/report/infrastructure/persistence/fixture/JpaAnswerReportRepositoryFixture.java @@ -115,8 +115,8 @@ void setUp() { final Question 질문1 = new Question(경매, 질문자, "질문드립니다."); final Question 질문2 = new Question(경매, 질문자, "질문드립니다."); - 답변 = new Answer("답변드립니다."); - 이미_신고된_답변 = new Answer("답변드립니다."); + 답변 = new Answer(판매자, "답변드립니다."); + 이미_신고된_답변 = new Answer(판매자, "답변드립니다."); 질문1.addAnswer(답변); 질문2.addAnswer(이미_신고된_답변); diff --git a/backend/ddang/src/test/java/com/ddang/ddang/report/infrastructure/persistence/fixture/JpaQuestionReportRepositoryFixture.java b/backend/ddang/src/test/java/com/ddang/ddang/report/infrastructure/persistence/fixture/JpaQuestionReportRepositoryImplFixture.java similarity index 74% rename from backend/ddang/src/test/java/com/ddang/ddang/report/infrastructure/persistence/fixture/JpaQuestionReportRepositoryFixture.java rename to backend/ddang/src/test/java/com/ddang/ddang/report/infrastructure/persistence/fixture/JpaQuestionReportRepositoryImplFixture.java index cae501164..06f52d338 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/report/infrastructure/persistence/fixture/JpaQuestionReportRepositoryFixture.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/report/infrastructure/persistence/fixture/JpaQuestionReportRepositoryImplFixture.java @@ -3,7 +3,10 @@ import com.ddang.ddang.auction.domain.Auction; import com.ddang.ddang.auction.domain.BidUnit; import com.ddang.ddang.auction.domain.Price; +import com.ddang.ddang.auction.domain.repository.AuctionRepository; +import com.ddang.ddang.auction.infrastructure.persistence.AuctionRepositoryImpl; import com.ddang.ddang.auction.infrastructure.persistence.JpaAuctionRepository; +import com.ddang.ddang.auction.infrastructure.persistence.QuerydslAuctionRepository; import com.ddang.ddang.category.domain.Category; import com.ddang.ddang.category.infrastructure.persistence.JpaCategoryRepository; import com.ddang.ddang.image.domain.AuctionImage; @@ -11,10 +14,15 @@ import com.ddang.ddang.qna.domain.Question; import com.ddang.ddang.qna.infrastructure.JpaQuestionRepository; import com.ddang.ddang.report.domain.QuestionReport; +import com.ddang.ddang.report.domain.repository.QuestionReportRepository; import com.ddang.ddang.report.infrastructure.persistence.JpaQuestionReportRepository; +import com.ddang.ddang.report.infrastructure.persistence.QuestionReportRepositoryImpl; import com.ddang.ddang.user.domain.Reliability; import com.ddang.ddang.user.domain.User; +import com.ddang.ddang.user.domain.repository.UserRepository; import com.ddang.ddang.user.infrastructure.persistence.JpaUserRepository; +import com.ddang.ddang.user.infrastructure.persistence.UserRepositoryImpl; +import com.querydsl.jpa.impl.JPAQueryFactory; import jakarta.persistence.EntityManager; import jakarta.persistence.PersistenceContext; import org.junit.jupiter.api.BeforeEach; @@ -24,25 +32,22 @@ import java.util.List; @SuppressWarnings("NonAsciiCharacters") -public class JpaQuestionReportRepositoryFixture { +public class JpaQuestionReportRepositoryImplFixture { @PersistenceContext private EntityManager em; - @Autowired - private JpaUserRepository userRepository; - @Autowired private JpaCategoryRepository categoryRepository; - @Autowired - private JpaAuctionRepository auctionRepository; - @Autowired private JpaQuestionRepository questionRepository; - @Autowired - private JpaQuestionReportRepository questionReportRepository; + private UserRepository userRepository; + + private AuctionRepository auctionRepository; + + private QuestionReportRepository questionReportRepository; protected User 신고자; protected Question 질문; @@ -54,7 +59,16 @@ public class JpaQuestionReportRepositoryFixture { protected QuestionReport 질문_신고4; @BeforeEach - void setUp() { + void setUpFixture( + @Autowired final JPAQueryFactory jpaQueryFactory, + @Autowired final JpaUserRepository jpaUserRepository, + @Autowired final JpaAuctionRepository jpaAuctionRepository, + @Autowired final JpaQuestionReportRepository jpaQuestionReportRepository + ) { + userRepository = new UserRepositoryImpl(jpaUserRepository); + auctionRepository = new AuctionRepositoryImpl(jpaAuctionRepository, new QuerydslAuctionRepository(jpaQueryFactory)); + questionReportRepository = new QuestionReportRepositoryImpl(jpaQuestionReportRepository); + final ProfileImage 프로필_이미지 = new ProfileImage("프로필.jpg", "프로필.jpg"); final User 판매자 = User.builder() .name("판매자") @@ -116,13 +130,21 @@ void setUp() { 질문_신고3 = new QuestionReport(신고자3, 이미_신고한_질문, "신고합니다"); 질문_신고4 = new QuestionReport(신고자4, 이미_신고한_질문, "신고합니다"); - userRepository.saveAll(List.of(판매자, 질문자, 신고자, 신고자2, 신고자3, 신고자4)); + userRepository.save(판매자); + userRepository.save(질문자); + userRepository.save(신고자); + userRepository.save(신고자2); + userRepository.save(신고자3); + userRepository.save(신고자4); categoryRepository.saveAll(List.of(전자기기_카테고리, 전자기기_서브_노트북_카테고리)); auctionRepository.save(경매); questionRepository.saveAll(List.of(질문, 이미_신고한_질문)); - questionReportRepository.saveAll(List.of(질문_신고1, 질문_신고2, 질문_신고3, 질문_신고4)); + questionReportRepository.save(질문_신고1); + questionReportRepository.save(질문_신고2); + questionReportRepository.save(질문_신고3); + questionReportRepository.save(질문_신고4); em.flush(); em.clear(); diff --git a/backend/ddang/src/test/java/com/ddang/ddang/report/infrastructure/persistence/fixture/QuestionReportRepositoryImplFixture.java b/backend/ddang/src/test/java/com/ddang/ddang/report/infrastructure/persistence/fixture/QuestionReportRepositoryImplFixture.java new file mode 100644 index 000000000..458fe7f2e --- /dev/null +++ b/backend/ddang/src/test/java/com/ddang/ddang/report/infrastructure/persistence/fixture/QuestionReportRepositoryImplFixture.java @@ -0,0 +1,144 @@ +package com.ddang.ddang.report.infrastructure.persistence.fixture; + +import com.ddang.ddang.auction.domain.Auction; +import com.ddang.ddang.auction.domain.BidUnit; +import com.ddang.ddang.auction.domain.Price; +import com.ddang.ddang.auction.domain.repository.AuctionRepository; +import com.ddang.ddang.auction.infrastructure.persistence.AuctionRepositoryImpl; +import com.ddang.ddang.auction.infrastructure.persistence.JpaAuctionRepository; +import com.ddang.ddang.auction.infrastructure.persistence.QuerydslAuctionRepository; +import com.ddang.ddang.category.domain.Category; +import com.ddang.ddang.category.infrastructure.persistence.JpaCategoryRepository; +import com.ddang.ddang.image.domain.AuctionImage; +import com.ddang.ddang.image.domain.ProfileImage; +import com.ddang.ddang.qna.domain.Question; +import com.ddang.ddang.qna.infrastructure.JpaQuestionRepository; +import com.ddang.ddang.report.domain.QuestionReport; +import com.ddang.ddang.report.domain.repository.QuestionReportRepository; +import com.ddang.ddang.report.infrastructure.persistence.JpaQuestionReportRepository; +import com.ddang.ddang.report.infrastructure.persistence.QuestionReportRepositoryImpl; +import com.ddang.ddang.user.domain.Reliability; +import com.ddang.ddang.user.domain.User; +import com.ddang.ddang.user.domain.repository.UserRepository; +import com.ddang.ddang.user.infrastructure.persistence.JpaUserRepository; +import com.ddang.ddang.user.infrastructure.persistence.UserRepositoryImpl; +import com.querydsl.jpa.impl.JPAQueryFactory; +import org.junit.jupiter.api.BeforeEach; +import org.springframework.beans.factory.annotation.Autowired; + +import java.time.LocalDateTime; +import java.util.List; + +@SuppressWarnings("NonAsciiCharacters") +public class QuestionReportRepositoryImplFixture { + + @Autowired + private JpaCategoryRepository categoryRepository; + + @Autowired + private JpaQuestionRepository questionRepository; + + private UserRepository userRepository; + + private AuctionRepository auctionRepository; + + private QuestionReportRepository questionReportRepository; + + protected User 신고자; + protected Question 질문; + protected Question 이미_신고한_질문; + protected String 신고_내용 = "신고합니다."; + protected QuestionReport 질문_신고1; + protected QuestionReport 질문_신고2; + protected QuestionReport 질문_신고3; + protected QuestionReport 질문_신고4; + + @BeforeEach + void setUpFixture( + @Autowired final JPAQueryFactory jpaQueryFactory, + @Autowired final JpaUserRepository jpaUserRepository, + @Autowired final JpaAuctionRepository jpaAuctionRepository, + @Autowired final JpaQuestionReportRepository jpaQuestionReportRepository + ) { + userRepository = new UserRepositoryImpl(jpaUserRepository); + auctionRepository = new AuctionRepositoryImpl(jpaAuctionRepository, new QuerydslAuctionRepository(jpaQueryFactory)); + questionReportRepository = new QuestionReportRepositoryImpl(jpaQuestionReportRepository); + + final ProfileImage 프로필_이미지 = new ProfileImage("프로필.jpg", "프로필.jpg"); + final User 판매자 = User.builder() + .name("판매자") + .profileImage(프로필_이미지) + .reliability(new Reliability(4.7d)) + .oauthId("12345") + .build(); + final User 질문자 = User.builder() + .name("질문자") + .profileImage(프로필_이미지) + .reliability(new Reliability(4.7d)) + .oauthId("12346") + .build(); + 신고자 = User.builder() + .name("신고자") + .profileImage(프로필_이미지) + .reliability(new Reliability(4.7d)) + .oauthId("12347") + .build(); + final User 신고자2 = User.builder() + .name("신고자2") + .profileImage(프로필_이미지) + .reliability(new Reliability(4.7d)) + .oauthId("12348") + .build(); + final User 신고자3 = User.builder() + .name("신고자3") + .profileImage(프로필_이미지) + .reliability(new Reliability(4.7d)) + .oauthId("12349") + .build(); + final User 신고자4 = User.builder() + .name("신고자4") + .profileImage(프로필_이미지) + .reliability(new Reliability(4.7d)) + .oauthId("12350") + .build(); + + final Category 전자기기_카테고리 = new Category("전자기기"); + final Category 전자기기_서브_노트북_카테고리 = new Category("노트북 카테고리"); + 전자기기_카테고리.addSubCategory(전자기기_서브_노트북_카테고리); + final AuctionImage 경매_이미지 = new AuctionImage("경매이미지.jpg", "경매이미지.jpg"); + final Auction 경매 = Auction.builder() + .seller(판매자) + .title("경매 상품") + .description("이것은 경매 상품입니다.") + .subCategory(전자기기_서브_노트북_카테고리) + .bidUnit(new BidUnit(1_000)) + .startPrice(new Price(1_000)) + .closingTime(LocalDateTime.now()) + .build(); + 경매.addAuctionImages(List.of(경매_이미지)); + + 질문 = new Question(경매, 질문자, "질문드립니다."); + 이미_신고한_질문 = new Question(경매, 질문자, "질문드립니다."); + + 질문_신고1 = new QuestionReport(신고자, 이미_신고한_질문, "신고합니다"); + 질문_신고2 = new QuestionReport(신고자2, 이미_신고한_질문, "신고합니다"); + 질문_신고3 = new QuestionReport(신고자3, 이미_신고한_질문, "신고합니다"); + 질문_신고4 = new QuestionReport(신고자4, 이미_신고한_질문, "신고합니다"); + + userRepository.save(판매자); + userRepository.save(질문자); + userRepository.save(신고자); + userRepository.save(신고자2); + userRepository.save(신고자3); + userRepository.save(신고자4); + + categoryRepository.saveAll(List.of(전자기기_카테고리, 전자기기_서브_노트북_카테고리)); + auctionRepository.save(경매); + + questionRepository.saveAll(List.of(질문, 이미_신고한_질문)); + questionReportRepository.save(질문_신고1); + questionReportRepository.save(질문_신고2); + questionReportRepository.save(질문_신고3); + questionReportRepository.save(질문_신고4); + } +} diff --git a/backend/ddang/src/test/java/com/ddang/ddang/review/application/ReviewServiceTest.java b/backend/ddang/src/test/java/com/ddang/ddang/review/application/ReviewServiceTest.java index 65307b708..72e5bc32a 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/review/application/ReviewServiceTest.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/review/application/ReviewServiceTest.java @@ -17,6 +17,7 @@ import java.util.List; +import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; @IsolateDatabase @@ -28,15 +29,12 @@ class ReviewServiceTest extends ReviewServiceFixture { ReviewService reviewService; @Test - void 평가를_등록한고_평가_상대의_신뢰도를_갱신한다() { + void 평가를_등록한다() { // when final Long actual = reviewService.create(구매자에_대한_평가_등록을_위한_DTO); // then - SoftAssertions.assertSoftly(softAssertions -> { - softAssertions.assertThat(actual).isPositive(); - softAssertions.assertThat(구매자.getReliability().getValue()).isEqualTo(구매자가_새로운_평가_점수를_받고난_후의_신뢰도_점수); - }); + assertThat(actual).isPositive(); } @Test diff --git a/backend/ddang/src/test/java/com/ddang/ddang/review/application/fixture/ReviewServiceFixture.java b/backend/ddang/src/test/java/com/ddang/ddang/review/application/fixture/ReviewServiceFixture.java index 510d1ea42..d9ca8775c 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/review/application/fixture/ReviewServiceFixture.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/review/application/fixture/ReviewServiceFixture.java @@ -3,7 +3,7 @@ import com.ddang.ddang.auction.domain.Auction; import com.ddang.ddang.auction.domain.BidUnit; import com.ddang.ddang.auction.domain.Price; -import com.ddang.ddang.auction.infrastructure.persistence.JpaAuctionRepository; +import com.ddang.ddang.auction.domain.repository.AuctionRepository; import com.ddang.ddang.category.domain.Category; import com.ddang.ddang.category.infrastructure.persistence.JpaCategoryRepository; import com.ddang.ddang.image.domain.AuctionImage; @@ -11,10 +11,10 @@ import com.ddang.ddang.review.application.dto.CreateReviewDto; import com.ddang.ddang.review.domain.Review; import com.ddang.ddang.review.domain.Score; -import com.ddang.ddang.review.infrastructure.persistence.JpaReviewRepository; +import com.ddang.ddang.review.domain.repository.ReviewRepository; import com.ddang.ddang.user.domain.Reliability; import com.ddang.ddang.user.domain.User; -import com.ddang.ddang.user.infrastructure.persistence.JpaUserRepository; +import com.ddang.ddang.user.domain.repository.UserRepository; import org.junit.jupiter.api.BeforeEach; import org.springframework.beans.factory.annotation.Autowired; @@ -28,20 +28,17 @@ public class ReviewServiceFixture { private JpaCategoryRepository categoryRepository; @Autowired - private JpaUserRepository userRepository; + private UserRepository userRepository; @Autowired - private JpaAuctionRepository auctionRepository; + private AuctionRepository auctionRepository; @Autowired - private JpaReviewRepository reviewRepository; + private ReviewRepository reviewRepository; - private Double 구매자가_판매자1에게_받은_평가_점수 = 5.0d; - private Double 구매자가_판매자2에게_받은_평가_점수 = 1.0d; - private Double 구매자가_받을_새로운_평가_점수 = 4.5d; - - protected Double 구매자가_새로운_평가_점수를_받고난_후의_신뢰도_점수 = - (구매자가_판매자1에게_받은_평가_점수 + 구매자가_판매자2에게_받은_평가_점수 + 구매자가_받을_새로운_평가_점수) / 3; + private double 구매자가_판매자1에게_받은_평가_점수 = 5.0d; + private double 구매자가_판매자2에게_받은_평가_점수 = 1.0d; + private double 구매자가_받을_새로운_평가_점수 = 4.5d; protected Long 존재하지_않는_사용자 = -999L; protected User 판매자1; protected User 판매자2; @@ -108,7 +105,11 @@ void setUp() { .reliability(new Reliability(4.7d)) .oauthId("12347") .build(); - userRepository.saveAll(List.of(판매자1, 판매자2, 평가_안한_경매_판매자, 구매자, 경매_참여자가_아닌_사용자)); + userRepository.save(판매자1); + userRepository.save(판매자2); + userRepository.save(평가_안한_경매_판매자); + userRepository.save(구매자); + userRepository.save(경매_참여자가_아닌_사용자); 판매자1이_평가한_경매 = Auction.builder() .seller(판매자1) @@ -140,7 +141,9 @@ void setUp() { 판매자1이_평가한_경매.addAuctionImages(List.of(경매1_대표_이미지)); 판매자2가_평가한_경매.addAuctionImages(List.of(경매2_대표_이미지)); 평가_안한_경매.addAuctionImages(List.of(평가_안한_경매_대표_이미지)); - auctionRepository.saveAll(List.of(판매자1이_평가한_경매, 판매자2가_평가한_경매, 평가_안한_경매)); + auctionRepository.save(판매자1이_평가한_경매); + auctionRepository.save(판매자2가_평가한_경매); + auctionRepository.save(평가_안한_경매); 구매자가_판매자1에게_받은_평가 = Review.builder() .auction(판매자1이_평가한_경매) @@ -156,7 +159,9 @@ void setUp() { .content("별로다.") .score(new Score(구매자가_판매자2에게_받은_평가_점수)) .build(); - reviewRepository.saveAll(List.of(구매자가_판매자1에게_받은_평가, 구매자가_판매자2에게_받은_평가)); + + reviewRepository.save(구매자가_판매자1에게_받은_평가); + reviewRepository.save(구매자가_판매자2에게_받은_평가); 구매자가_이전까지_받은_평가_총2개 = List.of(구매자가_판매자1에게_받은_평가, 구매자가_판매자2에게_받은_평가); diff --git a/backend/ddang/src/test/java/com/ddang/ddang/review/domain/ReviewsTest.java b/backend/ddang/src/test/java/com/ddang/ddang/review/domain/ReviewsTest.java new file mode 100644 index 000000000..034f69eef --- /dev/null +++ b/backend/ddang/src/test/java/com/ddang/ddang/review/domain/ReviewsTest.java @@ -0,0 +1,103 @@ +package com.ddang.ddang.review.domain; + +import com.ddang.ddang.review.domain.fixture.ReviewsFixture; +import com.ddang.ddang.user.domain.User; +import org.assertj.core.api.SoftAssertions; +import org.junit.jupiter.api.Test; + +import java.util.Collections; +import java.util.List; +import java.util.Set; + +import static org.assertj.core.api.Assertions.assertThat; + +class ReviewsTest extends ReviewsFixture { + + @Test + void 리뷰들의_점수를_모두_더해서_반환한다() { + // given + final Reviews reviews = new Reviews(List.of(사용자1의_평가1, 사용자1의_평가2)); + + // when + final double actual = reviews.addAllReviewScore(); + + // then + assertThat(actual).isEqualTo(평가1_점수 + 평가2_점수); + } + + @Test + void 리뷰들의_개수를_반환한다() { + // given + final Reviews reviews = new Reviews(List.of(사용자1의_평가1, 사용자1의_평가2)); + + // when + final int actual = reviews.size(); + + // then + assertThat(actual).isEqualTo(2); + } + + @Test + void 리뷰가_없다면_참을_반환한다() { + // given + final Reviews reviews = new Reviews(Collections.emptyList()); + + // when + final boolean actual = reviews.isEmpty(); + + // then + assertThat(actual).isTrue(); + } + + @Test + void 리뷰가_존재한다면_거짓을_반환한다() { + // given + final Reviews reviews = new Reviews(List.of(사용자1의_평가1, 사용자1의_평가2)); + + // when + final boolean actual = reviews.isEmpty(); + + // then + assertThat(actual).isFalse(); + } + + @Test + void 평가_대상자_목록을_중복_없이_반환한다() { + // given + final Reviews reviews = new Reviews(List.of(사용자1의_평가1, 사용자1의_평가2, 사용자2의_평가1, 사용자2의_평가2)); + + // when + final Set actual = reviews.findReviewTargets(); + + // then + assertThat(actual).containsExactlyInAnyOrder(사용자1, 사용자2); + } + + @Test + void 평가_대상_사용자가_받은_평가_목록을_반환한다() { + // given + final Reviews reviews = new Reviews(List.of(사용자1의_평가1, 사용자1의_평가2, 사용자2의_평가1, 사용자2의_평가2)); + + // when + final List actual = reviews.findReviewsByTarget(사용자1); + + // then + SoftAssertions.assertSoftly(softAssertions -> { + softAssertions.assertThat(actual).hasSize(2); + softAssertions.assertThat(actual.get(0)).isEqualTo(사용자1의_평가1); + softAssertions.assertThat(actual.get(1)).isEqualTo(사용자1의_평가2); + }); + } + + @Test + void 평가_목록_중_가장_마지막_평가의_아이디를_반환한다() { + // given + final Reviews reviews = new Reviews(List.of(사용자1의_평가1, 사용자1의_평가2, 사용자2의_평가1, 사용자2의_평가2)); + + // when + final Long actual = reviews.findLastReviewId(); + + // then + assertThat(actual).isEqualTo(사용자2의_평가2.getId()); + } +} diff --git a/backend/ddang/src/test/java/com/ddang/ddang/review/domain/fixture/ReviewsFixture.java b/backend/ddang/src/test/java/com/ddang/ddang/review/domain/fixture/ReviewsFixture.java new file mode 100644 index 000000000..15cf0673b --- /dev/null +++ b/backend/ddang/src/test/java/com/ddang/ddang/review/domain/fixture/ReviewsFixture.java @@ -0,0 +1,61 @@ +package com.ddang.ddang.review.domain.fixture; + +import com.ddang.ddang.image.domain.ProfileImage; +import com.ddang.ddang.review.domain.Review; +import com.ddang.ddang.review.domain.Score; +import com.ddang.ddang.user.domain.Reliability; +import com.ddang.ddang.user.domain.User; +import org.junit.jupiter.api.BeforeEach; +import org.springframework.test.util.ReflectionTestUtils; + +@SuppressWarnings("NonAsciiCharacters") +public class ReviewsFixture { + + protected double 평가1_점수 = 1.5d; + protected double 평가2_점수 = 2.5d; + protected User 사용자1; + protected User 사용자2; + protected Review 사용자1의_평가1; + protected Review 사용자1의_평가2; + protected Review 사용자2의_평가1; + protected Review 사용자2의_평가2; + + @BeforeEach + void setUp() { + 사용자1 = User.builder() + .name("사용자1") + .profileImage(new ProfileImage("uplad.png", "store.png")) + .reliability(Reliability.INITIAL_RELIABILITY) + .oauthId("12345") + .build(); + 사용자2 = User.builder() + .name("사용자2") + .profileImage(new ProfileImage("uplad.png", "store.png")) + .reliability(Reliability.INITIAL_RELIABILITY) + .oauthId("12346") + .build(); + ReflectionTestUtils.setField(사용자1, "id", 1L); + ReflectionTestUtils.setField(사용자2, "id", 2L); + + 사용자1의_평가1 = Review.builder() + .target(사용자1) + .score(new Score(평가1_점수)) + .build(); + ReflectionTestUtils.setField(사용자1의_평가1, "id", 1L); + 사용자1의_평가2 = Review.builder() + .target(사용자1) + .score(new Score(평가2_점수)) + .build(); + ReflectionTestUtils.setField(사용자1의_평가1, "id", 2L); + 사용자2의_평가1 = Review.builder() + .target(사용자2) + .score(new Score(평가1_점수)) + .build(); + ReflectionTestUtils.setField(사용자2의_평가1, "id", 3L); + 사용자2의_평가2 = Review.builder() + .target(사용자2) + .score(new Score(평가2_점수)) + .build(); + ReflectionTestUtils.setField(사용자2의_평가2, "id", 4L); + } +} diff --git a/backend/ddang/src/test/java/com/ddang/ddang/review/infrastructure/persistence/JpaReviewRepositoryTest.java b/backend/ddang/src/test/java/com/ddang/ddang/review/infrastructure/persistence/JpaReviewRepositoryTest.java index 1a491874c..a4f86c7d3 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/review/infrastructure/persistence/JpaReviewRepositoryTest.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/review/infrastructure/persistence/JpaReviewRepositoryTest.java @@ -25,28 +25,22 @@ @SuppressWarnings("NonAsciiCharacters") class JpaReviewRepositoryTest extends JpaReviewRepositoryFixture { - @PersistenceContext - EntityManager em; - @Autowired - JpaReviewRepository reviewRepository; + JpaReviewRepository jpaReviewRepository; @Test void 평가를_저장한다() { // when - reviewRepository.save(저장하려는_평가); + jpaReviewRepository.save(저장하려는_평가); // then - em.flush(); - em.clear(); - assertThat(저장하려는_평가.getId()).isPositive(); } @Test void 지정한_경매_아이디와_작성자_아이디를_포함하는_평가가_존재하면_참을_반환한다() { // when - final boolean actual = reviewRepository.existsByAuctionIdAndWriterId(판매자1이_평가한_경매.getId(), 판매자1.getId()); + final boolean actual = jpaReviewRepository.existsByAuctionIdAndWriterId(판매자1이_평가한_경매.getId(), 판매자1.getId()); // then assertThat(actual).isTrue(); @@ -55,7 +49,7 @@ class JpaReviewRepositoryTest extends JpaReviewRepositoryFixture { @Test void 지정한_채팅방_아이디를_포함하는_평가가_존재하지_않는다면_거짓을_반환한다() { // when - final boolean actual = reviewRepository.existsByAuctionIdAndWriterId(평가_안한_경매.getId(), 평가_안한_경매_판매자.getId()); + final boolean actual = jpaReviewRepository.existsByAuctionIdAndWriterId(평가_안한_경매.getId(), 평가_안한_경매_판매자.getId()); // then assertThat(actual).isFalse(); @@ -64,7 +58,7 @@ class JpaReviewRepositoryTest extends JpaReviewRepositoryFixture { @Test void 지정한_아이디가_평가_대상_아이디에_해당하는_평가_목록을_최신순으로_조회한다() { // when - final List actual = reviewRepository.findAllByTargetId(구매자.getId()); + final List actual = jpaReviewRepository.findAllByTargetId(구매자.getId()); // then SoftAssertions.assertSoftly(softAssertions -> { @@ -78,7 +72,7 @@ class JpaReviewRepositoryTest extends JpaReviewRepositoryFixture { void 지정한_경매_아이디와_작성자_아이디가_해당하는_평가가_존재한다면_optional에_넣어_반환한다() { // when final Optional actual = - reviewRepository.findByAuctionIdAndWriterId(판매자1이_평가한_경매.getId(), 판매자1.getId()); + jpaReviewRepository.findByAuctionIdAndWriterId(판매자1이_평가한_경매.getId(), 판매자1.getId()); // then assertThat(actual).contains(구매자가_판매자1에게_받은_평가); @@ -88,9 +82,21 @@ class JpaReviewRepositoryTest extends JpaReviewRepositoryFixture { void 지정한_경매_아이디와_작성자_아이디가_해당하는_평가가_존재하지_않는다면_빈_optional을_반환한다() { // when final Optional actual = - reviewRepository.findByAuctionIdAndWriterId(평가_안한_경매.getId(), 평가_안한_경매_판매자.getId()); + jpaReviewRepository.findByAuctionIdAndWriterId(평가_안한_경매.getId(), 평가_안한_경매_판매자.getId()); // then assertThat(actual).isEmpty(); } + + @Test + void 지정한_평가_아이디보다_아이디가_큰_평가_목록을_조회한다() { + // when + final List actual = jpaReviewRepository.findAllByIdGreaterThan(구매자가_판매자1에게_받은_평가.getId()); + + // then + SoftAssertions.assertSoftly(softAssertions -> { + softAssertions.assertThat(actual).hasSize(1); + softAssertions.assertThat(actual.get(0)).isEqualTo(구매자가_판매자2에게_받은_평가); + }); + } } diff --git a/backend/ddang/src/test/java/com/ddang/ddang/review/infrastructure/persistence/ReviewRepositoryImplTest.java b/backend/ddang/src/test/java/com/ddang/ddang/review/infrastructure/persistence/ReviewRepositoryImplTest.java new file mode 100644 index 000000000..e69b34a68 --- /dev/null +++ b/backend/ddang/src/test/java/com/ddang/ddang/review/infrastructure/persistence/ReviewRepositoryImplTest.java @@ -0,0 +1,106 @@ +package com.ddang.ddang.review.infrastructure.persistence; + +import com.ddang.ddang.configuration.JpaConfiguration; +import com.ddang.ddang.configuration.QuerydslConfiguration; +import com.ddang.ddang.review.domain.Review; +import com.ddang.ddang.review.domain.repository.ReviewRepository; +import com.ddang.ddang.review.infrastructure.persistence.fixture.ReviewRepositoryImplFixture; +import org.assertj.core.api.SoftAssertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.context.annotation.Import; + +import java.util.List; +import java.util.Optional; + +import static org.assertj.core.api.Assertions.assertThat; + +@DataJpaTest +@Import({JpaConfiguration.class, QuerydslConfiguration.class}) +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) +@SuppressWarnings("NonAsciiCharacters") +class ReviewRepositoryImplTest extends ReviewRepositoryImplFixture { + + ReviewRepository reviewRepository; + + @BeforeEach + void setUp(@Autowired final JpaReviewRepository jpaReviewRepository) { + reviewRepository = new ReviewRepositoryImpl(jpaReviewRepository); + } + + @Test + void 평가를_저장한다() { + // when + reviewRepository.save(저장하려는_평가); + + // then + assertThat(저장하려는_평가.getId()).isPositive(); + } + + @Test + void 지정한_경매_아이디와_작성자_아이디를_포함하는_평가가_존재하면_참을_반환한다() { + // when + final boolean actual = reviewRepository.existsByAuctionIdAndWriterId(판매자1이_평가한_경매.getId(), 판매자1.getId()); + + // then + assertThat(actual).isTrue(); + } + + @Test + void 지정한_채팅방_아이디를_포함하는_평가가_존재하지_않는다면_거짓을_반환한다() { + // when + final boolean actual = reviewRepository.existsByAuctionIdAndWriterId(평가_안한_경매.getId(), 평가_안한_경매_판매자.getId()); + + // then + assertThat(actual).isFalse(); + } + + @Test + void 지정한_아이디가_평가_대상_아이디에_해당하는_평가_목록을_최신순으로_조회한다() { + // when + final List actual = reviewRepository.findAllByTargetId(구매자.getId()); + + // then + SoftAssertions.assertSoftly(softAssertions -> { + softAssertions.assertThat(actual).hasSize(2); + softAssertions.assertThat(actual.get(0)).isEqualTo(구매자가_판매자2에게_받은_평가); + softAssertions.assertThat(actual.get(1)).isEqualTo(구매자가_판매자1에게_받은_평가); + }); + } + + @Test + void 지정한_경매_아이디와_작성자_아이디가_해당하는_평가가_존재한다면_optional에_넣어_반환한다() { + // when + final Optional actual = + reviewRepository.findByAuctionIdAndWriterId(판매자1이_평가한_경매.getId(), 판매자1.getId()); + + // then + assertThat(actual).contains(구매자가_판매자1에게_받은_평가); + } + + @Test + void 지정한_경매_아이디와_작성자_아이디가_해당하는_평가가_존재하지_않는다면_빈_optional을_반환한다() { + // when + final Optional actual = + reviewRepository.findByAuctionIdAndWriterId(평가_안한_경매.getId(), 평가_안한_경매_판매자.getId()); + + // then + assertThat(actual).isEmpty(); + } + + @Test + void 지정한_평가_아이디보다_아이디가_큰_평가_목록을_조회한다() { + // when + final List actual = reviewRepository.findAllByIdGreaterThan(구매자가_판매자1에게_받은_평가.getId()); + + // then + SoftAssertions.assertSoftly(softAssertions -> { + softAssertions.assertThat(actual).hasSize(1); + softAssertions.assertThat(actual.get(0)).isEqualTo(구매자가_판매자2에게_받은_평가); + }); + } +} diff --git a/backend/ddang/src/test/java/com/ddang/ddang/review/infrastructure/persistence/fixture/JpaReviewRepositoryFixture.java b/backend/ddang/src/test/java/com/ddang/ddang/review/infrastructure/persistence/fixture/JpaReviewRepositoryFixture.java index fae340ff0..214a2ac84 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/review/infrastructure/persistence/fixture/JpaReviewRepositoryFixture.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/review/infrastructure/persistence/fixture/JpaReviewRepositoryFixture.java @@ -40,7 +40,6 @@ public class JpaReviewRepositoryFixture { @Autowired private JpaReviewRepository reviewRepository; - protected User 판매자1; protected User 판매자2; protected User 평가_안한_경매_판매자; diff --git a/backend/ddang/src/test/java/com/ddang/ddang/review/infrastructure/persistence/fixture/ReviewRepositoryImplFixture.java b/backend/ddang/src/test/java/com/ddang/ddang/review/infrastructure/persistence/fixture/ReviewRepositoryImplFixture.java new file mode 100644 index 000000000..55e9a4554 --- /dev/null +++ b/backend/ddang/src/test/java/com/ddang/ddang/review/infrastructure/persistence/fixture/ReviewRepositoryImplFixture.java @@ -0,0 +1,169 @@ +package com.ddang.ddang.review.infrastructure.persistence.fixture; + +import com.ddang.ddang.auction.domain.Auction; +import com.ddang.ddang.auction.domain.BidUnit; +import com.ddang.ddang.auction.domain.Price; +import com.ddang.ddang.auction.domain.repository.AuctionRepository; +import com.ddang.ddang.auction.infrastructure.persistence.AuctionRepositoryImpl; +import com.ddang.ddang.auction.infrastructure.persistence.JpaAuctionRepository; +import com.ddang.ddang.auction.infrastructure.persistence.QuerydslAuctionRepository; +import com.ddang.ddang.category.domain.Category; +import com.ddang.ddang.category.infrastructure.persistence.JpaCategoryRepository; +import com.ddang.ddang.image.domain.AuctionImage; +import com.ddang.ddang.image.domain.ProfileImage; +import com.ddang.ddang.review.domain.Review; +import com.ddang.ddang.review.domain.Score; +import com.ddang.ddang.review.domain.repository.ReviewRepository; +import com.ddang.ddang.review.infrastructure.persistence.JpaReviewRepository; +import com.ddang.ddang.review.infrastructure.persistence.ReviewRepositoryImpl; +import com.ddang.ddang.user.domain.Reliability; +import com.ddang.ddang.user.domain.User; +import com.ddang.ddang.user.domain.repository.UserRepository; +import com.ddang.ddang.user.infrastructure.persistence.JpaUserRepository; +import com.ddang.ddang.user.infrastructure.persistence.UserRepositoryImpl; +import com.querydsl.jpa.impl.JPAQueryFactory; +import org.junit.jupiter.api.BeforeEach; +import org.springframework.beans.factory.annotation.Autowired; + +import java.time.LocalDateTime; +import java.util.List; + +@SuppressWarnings("NonAsciiCharacters") +public class ReviewRepositoryImplFixture { + + @Autowired + private JpaCategoryRepository categoryRepository; + + private UserRepository userRepository; + + private AuctionRepository auctionRepository; + + private ReviewRepository reviewRepository; + + protected User 판매자1; + protected User 판매자2; + protected User 평가_안한_경매_판매자; + protected User 구매자; + protected Auction 판매자1이_평가한_경매; + protected Auction 판매자2가_평가한_경매; + protected Auction 평가_안한_경매; + protected Review 저장하려는_평가; + protected Review 구매자가_판매자1에게_받은_평가; + protected Review 구매자가_판매자2에게_받은_평가; + + @BeforeEach + void fixtureSetUp( + @Autowired final JPAQueryFactory jpaQueryFactory, + @Autowired final JpaAuctionRepository jpaAuctionRepository, + @Autowired final JpaUserRepository jpaUserRepository, + @Autowired final JpaReviewRepository jpaReviewRepository + ) { + auctionRepository = new AuctionRepositoryImpl(jpaAuctionRepository, new QuerydslAuctionRepository(jpaQueryFactory)); + userRepository = new UserRepositoryImpl(jpaUserRepository); + reviewRepository = new ReviewRepositoryImpl(jpaReviewRepository); + + final Category 전자기기_카테고리 = new Category("전자기기"); + final Category 전자기기_서브_노트북_카테고리 = new Category("노트북 카테고리"); + 전자기기_카테고리.addSubCategory(전자기기_서브_노트북_카테고리); + categoryRepository.save(전자기기_카테고리); + + final ProfileImage 평가_안한_판매자_프로필_이미지 = new ProfileImage("no_review_seller_profile.png", "no_review_seller_profile.png"); + final ProfileImage 판매자1_프로필_이미지 = new ProfileImage("seller1_profile.png", "seller1_profile.png"); + final ProfileImage 판매자2_프로필_이미지 = new ProfileImage("seller2_profile.png", "seller2_profile.png"); + final ProfileImage 구매자_프로필_이미지 = new ProfileImage("buyer_profile.png", "buyer_profile.png"); + + 판매자1 = User.builder() + .name("판매자1") + .profileImage(판매자1_프로필_이미지) + .reliability(new Reliability(4.7d)) + .oauthId("12345") + .build(); + 판매자2 = User.builder() + .name("판매자2") + .profileImage(판매자2_프로필_이미지) + .reliability(new Reliability(4.7d)) + .oauthId("12346") + .build(); + 평가_안한_경매_판매자 = User.builder() + .name("평가 안한 판매자") + .profileImage(평가_안한_판매자_프로필_이미지) + .reliability(new Reliability(4.7d)) + .oauthId("12346") + .build(); + 구매자 = User.builder() + .name("구매자") + .profileImage(구매자_프로필_이미지) + .reliability(new Reliability(4.7d)) + .oauthId("12347") + .build(); + + userRepository.save(판매자1); + userRepository.save(판매자2); + userRepository.save(평가_안한_경매_판매자); + userRepository.save(구매자); + + final AuctionImage 경매1_대표_이미지 = new AuctionImage("경매1_대표_이미지.png", "경매1_대표_이미지.png"); + final AuctionImage 경매2_대표_이미지 = new AuctionImage("경매2_대표_이미지.png", "경매2_대표_이미지.png"); + final AuctionImage 평가_안한_경매_대표_이미지 = new AuctionImage("평가_안한_경매_대표_이미지.png", "평가_안한_경매_대표_이미지.png"); + + 판매자1이_평가한_경매 = Auction.builder() + .seller(판매자1) + .title("맥북") + .description("맥북 팔아요") + .subCategory(전자기기_서브_노트북_카테고리) + .startPrice(new Price(10_000)) + .bidUnit(new BidUnit(1_000)) + .closingTime(LocalDateTime.now()) + .build(); + 판매자2가_평가한_경매 = Auction.builder() + .seller(판매자2) + .title("맥북") + .description("맥북 팔아요") + .subCategory(전자기기_서브_노트북_카테고리) + .startPrice(new Price(10_000)) + .bidUnit(new BidUnit(1_000)) + .closingTime(LocalDateTime.now()) + .build(); + 평가_안한_경매 = Auction.builder() + .seller(판매자2) + .title("맥북") + .description("맥북 팔아요") + .subCategory(전자기기_서브_노트북_카테고리) + .startPrice(new Price(10_000)) + .bidUnit(new BidUnit(1_000)) + .closingTime(LocalDateTime.now()) + .build(); + + 판매자1이_평가한_경매.addAuctionImages(List.of(경매1_대표_이미지)); + 판매자2가_평가한_경매.addAuctionImages(List.of(경매2_대표_이미지)); + 평가_안한_경매.addAuctionImages(List.of(평가_안한_경매_대표_이미지)); + auctionRepository.save(판매자1이_평가한_경매); + auctionRepository.save(판매자2가_평가한_경매); + auctionRepository.save(평가_안한_경매); + + 저장하려는_평가 = Review.builder() + .auction(평가_안한_경매) + .writer(판매자1) + .target(구매자) + .content("친절하다.") + .score(new Score(5.0d)) + .build(); + 구매자가_판매자1에게_받은_평가 = Review.builder() + .auction(판매자1이_평가한_경매) + .writer(판매자1) + .target(구매자) + .content("친절하다.") + .score(new Score(5.0d)) + .build(); + 구매자가_판매자2에게_받은_평가 = Review.builder() + .auction(판매자2가_평가한_경매) + .writer(판매자2) + .target(구매자) + .content("별로다.") + .score(new Score(1.0d)) + .build(); + + reviewRepository.save(구매자가_판매자1에게_받은_평가); + reviewRepository.save(구매자가_판매자2에게_받은_평가); + } +} diff --git a/backend/ddang/src/test/java/com/ddang/ddang/user/application/UserServiceTest.java b/backend/ddang/src/test/java/com/ddang/ddang/user/application/UserServiceTest.java index 6db02f940..c68ffc576 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/user/application/UserServiceTest.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/user/application/UserServiceTest.java @@ -3,6 +3,7 @@ import com.ddang.ddang.configuration.IsolateDatabase; import com.ddang.ddang.image.domain.StoreImageProcessor; import com.ddang.ddang.user.application.dto.ReadUserDto; +import com.ddang.ddang.user.application.exception.AlreadyExistsNameException; import com.ddang.ddang.user.application.exception.UserNotFoundException; import com.ddang.ddang.user.application.fixture.UserServiceFixture; import org.assertj.core.api.SoftAssertions; @@ -78,6 +79,14 @@ class UserServiceTest extends UserServiceFixture { }); } + @Test + void 사용자_정보_수정시_동일한_이름이_이미_존재한다_예외가_발생한다() { + // when & then + assertThatThrownBy(() -> userService.updateById(사용자.getId(), 이미_존재하는_사용자_이름으로_수정_요청_dto)) + .isInstanceOf(AlreadyExistsNameException.class) + .hasMessage("이미 존재하는 닉네임입니다."); + } + @Test void 사용자_정보를_수정시_이미지만_수정한다() { // given diff --git a/backend/ddang/src/test/java/com/ddang/ddang/user/application/fixture/UserServiceFixture.java b/backend/ddang/src/test/java/com/ddang/ddang/user/application/fixture/UserServiceFixture.java index 11a3a18aa..c62e5bb4d 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/user/application/fixture/UserServiceFixture.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/user/application/fixture/UserServiceFixture.java @@ -5,7 +5,7 @@ import com.ddang.ddang.user.application.dto.UpdateUserDto; import com.ddang.ddang.user.domain.Reliability; import com.ddang.ddang.user.domain.User; -import com.ddang.ddang.user.infrastructure.persistence.JpaUserRepository; +import com.ddang.ddang.user.domain.repository.UserRepository; import org.junit.jupiter.api.BeforeEach; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; @@ -15,7 +15,7 @@ public class UserServiceFixture { @Autowired - private JpaUserRepository userRepository; + private UserRepository userRepository; protected Long 존재하지_않는_사용자_아이디 = -999L; @@ -25,18 +25,27 @@ public class UserServiceFixture { protected StoreImageDto 새로운_프로필_이미지_dto; protected UpdateUserDto 사용자_정보_수정_요청_dto; protected UpdateUserDto 사용자_이름만_수정_요청_dto; + protected UpdateUserDto 이미_존재하는_사용자_이름으로_수정_요청_dto; protected UpdateUserDto 사용자_이미지만_수정_요청_dto; @BeforeEach void setUp() { 프로필_이미지 = new ProfileImage("upload.png", "store.png"); + final String 이미_존재하는_사용자_이름 = "중복되는 이름"; + final User 이미_저장된_사용자 = User.builder() + .name(이미_존재하는_사용자_이름) + .profileImage(프로필_이미지) + .reliability(new Reliability(4.7d)) + .oauthId("12345") + .build(); 사용자 = User.builder() .name(사용자_이름) .profileImage(프로필_이미지) .reliability(new Reliability(4.7d)) - .oauthId("12345") + .oauthId("12346") .build(); + userRepository.save(이미_저장된_사용자); userRepository.save(사용자); final MockMultipartFile 새로운_이미지_파일 = new MockMultipartFile( @@ -49,6 +58,7 @@ void setUp() { 사용자_정보_수정_요청_dto = new UpdateUserDto("updateName", 새로운_이미지_파일); 사용자_이름만_수정_요청_dto = new UpdateUserDto("updateName", null); + 이미_존재하는_사용자_이름으로_수정_요청_dto = new UpdateUserDto(이미_존재하는_사용자_이름, null); 사용자_이미지만_수정_요청_dto = new UpdateUserDto(null, 새로운_이미지_파일); } } diff --git a/backend/ddang/src/test/java/com/ddang/ddang/user/application/schedule/ReliabilityUpdateSchedulingServiceTest.java b/backend/ddang/src/test/java/com/ddang/ddang/user/application/schedule/ReliabilityUpdateSchedulingServiceTest.java new file mode 100644 index 000000000..7952620af --- /dev/null +++ b/backend/ddang/src/test/java/com/ddang/ddang/user/application/schedule/ReliabilityUpdateSchedulingServiceTest.java @@ -0,0 +1,81 @@ +package com.ddang.ddang.user.application.schedule; + +import com.ddang.ddang.configuration.IsolateDatabase; +import com.ddang.ddang.user.application.schedule.fixture.ReliabilityUpdateSchedulingServiceFixture; +import com.ddang.ddang.user.domain.ReliabilityUpdateHistory; +import com.ddang.ddang.user.domain.UserReliability; +import com.ddang.ddang.user.domain.repository.ReliabilityUpdateHistoryRepository; +import com.ddang.ddang.user.domain.repository.UserReliabilityRepository; +import org.assertj.core.api.SoftAssertions; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +import java.util.Optional; + +import static org.assertj.core.api.Assertions.assertThat; + +@IsolateDatabase +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) +@SuppressWarnings("NonAsciiCharacters") +class ReliabilityUpdateSchedulingServiceTest extends ReliabilityUpdateSchedulingServiceFixture { + + @Autowired + ReliabilityUpdateSchedulingService reliabilityUpdateSchedulingService; + + @Autowired + ReliabilityUpdateHistoryRepository reliabilityUpdateHistoryRepository; + + @Autowired + UserReliabilityRepository userReliabilityRepository; + + @Test + void 사용자의_신뢰도를_갱신한다() { + // when + reliabilityUpdateSchedulingService.updateAllUserReliability(); + + // then + SoftAssertions.assertSoftly(softAssertions -> { + softAssertions.assertThat(구매자1_기존_평가_2개_새로운_평가_1개.getReliability()).isEqualTo(구매자1_최종_신뢰도); + softAssertions.assertThat(구매자2_기존_평가_3개_새로운_평가_2개.getReliability()).isEqualTo(구매자2_최종_신뢰도); + softAssertions.assertThat(구매자3_기존_평가_5개_새로운_평가_1개.getReliability()).isEqualTo(구매자3_최종_신뢰도); + softAssertions.assertThat(구매자4_기존_평가_없고_새로운_평가_1개.getReliability()).isEqualTo(구매자4_최종_신뢰도); + softAssertions.assertThat(구매자5_기존_평가_없고_새로운_평가_3개.getReliability()).isEqualTo(구매자5_최종_신뢰도); + }); + } + + @Test + void 사용자의_신뢰도를_갱신하면_사용자_신뢰도_반영_정보도_갱신된다() { + // when + reliabilityUpdateSchedulingService.updateAllUserReliability(); + + // then + final Optional actual1 = userReliabilityRepository.findByUserId(구매자1_기존_평가_2개_새로운_평가_1개.getId()); + final Optional actual2 = userReliabilityRepository.findByUserId(구매자2_기존_평가_3개_새로운_평가_2개.getId()); + final Optional actual3 = userReliabilityRepository.findByUserId(구매자3_기존_평가_5개_새로운_평가_1개.getId()); + final Optional actual4 = userReliabilityRepository.findByUserId(구매자4_기존_평가_없고_새로운_평가_1개.getId()); + final Optional actual5 = userReliabilityRepository.findByUserId(구매자5_기존_평가_없고_새로운_평가_3개.getId()); + final Optional actual6 = userReliabilityRepository.findByUserId(구매자6_기존_평가_없고_새로운_평가_없음.getId()); + + SoftAssertions.assertSoftly(softAssertions -> { + softAssertions.assertThat(actual1.get().getAppliedReviewCount()).isEqualTo(구매자1_최종_신뢰도_반영_개수); + softAssertions.assertThat(actual2.get().getAppliedReviewCount()).isEqualTo(구매자2_최종_신뢰도_반영_개수); + softAssertions.assertThat(actual3.get().getAppliedReviewCount()).isEqualTo(구매자3_최종_신뢰도_반영_개수); + softAssertions.assertThat(actual4.get().getAppliedReviewCount()).isEqualTo(구매자4_최종_신뢰도_반영_개수); + softAssertions.assertThat(actual5.get().getAppliedReviewCount()).isEqualTo(구매자5_최종_신뢰도_반영_개수); + softAssertions.assertThat(actual6).isEmpty(); + }); + } + + @Test + void 사용자_신뢰도_갱신_후에_마지막으로_적용된_평가의_아이디를_디비에_저장한다() { + // when + reliabilityUpdateSchedulingService.updateAllUserReliability(); + + // then + final Optional actual = reliabilityUpdateHistoryRepository.findFirstByOrderByIdDesc(); + + assertThat(actual.get().getLastAppliedReviewId()).isEqualTo(새로운_평가를_업데이트한_후의_마지막으로_적용된_평가의_아이디); + } +} diff --git a/backend/ddang/src/test/java/com/ddang/ddang/user/application/schedule/fixture/ReliabilityUpdateSchedulingServiceFixture.java b/backend/ddang/src/test/java/com/ddang/ddang/user/application/schedule/fixture/ReliabilityUpdateSchedulingServiceFixture.java new file mode 100644 index 000000000..bfeee8785 --- /dev/null +++ b/backend/ddang/src/test/java/com/ddang/ddang/user/application/schedule/fixture/ReliabilityUpdateSchedulingServiceFixture.java @@ -0,0 +1,369 @@ +package com.ddang.ddang.user.application.schedule.fixture; + +import com.ddang.ddang.auction.domain.Auction; +import com.ddang.ddang.auction.domain.repository.AuctionRepository; +import com.ddang.ddang.image.domain.ProfileImage; +import com.ddang.ddang.review.domain.Review; +import com.ddang.ddang.review.domain.Reviews; +import com.ddang.ddang.review.domain.Score; +import com.ddang.ddang.review.domain.repository.ReviewRepository; +import com.ddang.ddang.user.domain.Reliability; +import com.ddang.ddang.user.domain.ReliabilityUpdateHistory; +import com.ddang.ddang.user.domain.User; +import com.ddang.ddang.user.domain.UserReliability; +import com.ddang.ddang.user.domain.repository.ReliabilityUpdateHistoryRepository; +import com.ddang.ddang.user.domain.repository.UserReliabilityRepository; +import com.ddang.ddang.user.domain.repository.UserRepository; +import org.junit.jupiter.api.BeforeEach; +import org.springframework.beans.factory.annotation.Autowired; + +import java.util.List; + +@SuppressWarnings("NonAsciiCharacters") +public class ReliabilityUpdateSchedulingServiceFixture { + + @Autowired + private UserRepository userRepository; + + @Autowired + private AuctionRepository auctionRepository; + + @Autowired + private ReviewRepository reviewRepository; + + @Autowired + private UserReliabilityRepository userReliabilityRepository; + + @Autowired + private ReliabilityUpdateHistoryRepository reliabilityUpdateHistoryRepository; + + private double 구매자1_기존_신뢰도_점수 = 3.5d; + private double 구매자2_기존_신뢰도_점수 = 4.0d; + private double 구매자3_기존_신뢰도_점수 = 4.5d; + private double 구매자1_새로운_평가1_점수 = 5.0d; + private double 구매자2_새로운_평가1_점수 = 4.5d; + private double 구매자2_새로운_평가2_점수 = 5.0d; + private double 구매자3_새로운_평가1_점수 = 3.0d; + private double 구매자4_새로운_평가1_점수 = 3.5d; + private double 구매자5_새로운_평가1_점수 = 1.0d; + private double 구매자5_새로운_평가2_점수 = 2.0d; + private double 구매자5_새로운_평가3_점수 = 3.0d; + protected Long 기존에_적용된_평가_중_마지막_평가의_아이디 = 10L; + protected Reliability 구매자1_최종_신뢰도 = new Reliability(4.0d); + protected Reliability 구매자2_최종_신뢰도 = new Reliability(4.3d); + protected Reliability 구매자3_최종_신뢰도 = new Reliability(4.25d); + protected Reliability 구매자4_최종_신뢰도 = new Reliability(3.5d); + protected Reliability 구매자5_최종_신뢰도 = new Reliability(2.0d); + protected User 구매자1_기존_평가_2개_새로운_평가_1개; + protected User 구매자2_기존_평가_3개_새로운_평가_2개; + protected User 구매자3_기존_평가_5개_새로운_평가_1개; + protected User 구매자4_기존_평가_없고_새로운_평가_1개; + protected User 구매자5_기존_평가_없고_새로운_평가_3개; + protected User 구매자6_기존_평가_없고_새로운_평가_없음; + + protected int 구매자1_최종_신뢰도_반영_개수 = 3; + protected int 구매자2_최종_신뢰도_반영_개수 = 5; + protected int 구매자3_최종_신뢰도_반영_개수 = 6; + protected int 구매자4_최종_신뢰도_반영_개수 = 1; + protected int 구매자5_최종_신뢰도_반영_개수 = 3; + protected Review 구매자1_새로운_평가1; + protected Review 구매자2_새로운_평가1; + protected Review 구매자2_새로운_평가2; + protected Review 구매자3_새로운_평가1; + protected Review 구매자4_새로운_평가1; + protected Review 구매자5_새로운_평가1; + protected Review 구매자5_새로운_평가2; + protected Review 구매자5_새로운_평가3; + protected Long 새로운_평가를_업데이트한_후의_마지막으로_적용된_평가의_아이디 = 18L; + + + @BeforeEach + void setUp() { + final User 판매자 = User.builder() + .name("판매자") + .profileImage(new ProfileImage("upload.png", "updateImage.png")) + .reliability(Reliability.INITIAL_RELIABILITY) + .oauthId("1000") + .build(); + 구매자1_기존_평가_2개_새로운_평가_1개 = User.builder() + .name("구매자1") + .profileImage(new ProfileImage("upload.png", "updateImage.png")) + .reliability(Reliability.INITIAL_RELIABILITY) + .oauthId("1001") + .build(); + 구매자2_기존_평가_3개_새로운_평가_2개 = User.builder() + .name("구매자2") + .profileImage(new ProfileImage("upload.png", "updateImage.png")) + .reliability(Reliability.INITIAL_RELIABILITY) + .oauthId("1002") + .build(); + 구매자3_기존_평가_5개_새로운_평가_1개 = User.builder() + .name("구매자3") + .profileImage(new ProfileImage("upload.png", "updateImage.png")) + .reliability(Reliability.INITIAL_RELIABILITY) + .oauthId("1003") + .build(); + 구매자4_기존_평가_없고_새로운_평가_1개 = User.builder() + .name("구매자4") + .profileImage(new ProfileImage("upload.png", "updateImage.png")) + .reliability(Reliability.INITIAL_RELIABILITY) + .oauthId("1004") + .build(); + 구매자5_기존_평가_없고_새로운_평가_3개 = User.builder() + .name("구매자5") + .profileImage(new ProfileImage("upload.png", "updateImage.png")) + .reliability(Reliability.INITIAL_RELIABILITY) + .oauthId("1005") + .build(); + 구매자6_기존_평가_없고_새로운_평가_없음 = User.builder() + .name("구매자6") + .profileImage(new ProfileImage("upload.png", "updateImage.png")) + .reliability(Reliability.INITIAL_RELIABILITY) + .oauthId("1006") + .build(); + userRepository.save(판매자); + userRepository.save(구매자1_기존_평가_2개_새로운_평가_1개); + userRepository.save(구매자2_기존_평가_3개_새로운_평가_2개); + userRepository.save(구매자3_기존_평가_5개_새로운_평가_1개); + userRepository.save(구매자4_기존_평가_없고_새로운_평가_1개); + userRepository.save(구매자5_기존_평가_없고_새로운_평가_3개); + userRepository.save(구매자6_기존_평가_없고_새로운_평가_없음); + + final Auction 경매1 = Auction.builder() + .title("경매1") + .seller(판매자) + .build(); + final Auction 경매2 = Auction.builder() + .title("경매2") + .seller(판매자) + .build(); + final Auction 경매3 = Auction.builder() + .title("경매3") + .seller(판매자) + .build(); + final Auction 경매4 = Auction.builder() + .title("경매4") + .seller(판매자) + .build(); + final Auction 경매5 = Auction.builder() + .title("경매5") + .seller(판매자) + .build(); + final Auction 경매6 = Auction.builder() + .title("경매7") + .seller(판매자) + .build(); + final Auction 경매7 = Auction.builder() + .title("경매7") + .seller(판매자) + .build(); + final Auction 경매8 = Auction.builder() + .title("경매8") + .seller(판매자) + .build(); + final Auction 경매9 = Auction.builder() + .title("경매9") + .seller(판매자) + .build(); + final Auction 경매10 = Auction.builder() + .title("경매10") + .seller(판매자) + .build(); + final Auction 경매11 = Auction.builder() + .title("경매11") + .seller(판매자) + .build(); + final Auction 경매12 = Auction.builder() + .title("경매12") + .seller(판매자) + .build(); + final Auction 경매13 = Auction.builder() + .title("경매13") + .seller(판매자) + .build(); + final Auction 경매14 = Auction.builder() + .title("경매14") + .seller(판매자) + .build(); + final Auction 경매15 = Auction.builder() + .title("경매15") + .seller(판매자) + .build(); + final Auction 경매16 = Auction.builder() + .title("경매16") + .seller(판매자) + .build(); + final Auction 경매17 = Auction.builder() + .title("경매17") + .seller(판매자) + .build(); + final Auction 경매18 = Auction.builder() + .title("경매18") + .seller(판매자) + .build(); + + auctionRepository.save(경매1); + auctionRepository.save(경매2); + auctionRepository.save(경매3); + auctionRepository.save(경매4); + auctionRepository.save(경매5); + auctionRepository.save(경매6); + auctionRepository.save(경매7); + auctionRepository.save(경매8); + auctionRepository.save(경매9); + auctionRepository.save(경매10); + auctionRepository.save(경매11); + auctionRepository.save(경매12); + auctionRepository.save(경매13); + auctionRepository.save(경매14); + auctionRepository.save(경매15); + auctionRepository.save(경매16); + auctionRepository.save(경매17); + auctionRepository.save(경매18); + + final Review 구매자1_기존_평가1 = Review.builder() + .auction(경매1) + .writer(판매자) + .target(구매자1_기존_평가_2개_새로운_평가_1개) + .score(new Score(구매자1_기존_신뢰도_점수)) + .build(); + final Review 구매자1_기존_평가2 = Review.builder() + .auction(경매2) + .writer(판매자) + .target(구매자1_기존_평가_2개_새로운_평가_1개) + .score(new Score(구매자1_기존_신뢰도_점수)) + .build(); + final Review 구매자2_기존_평가1 = Review.builder() + .auction(경매3) + .writer(판매자) + .target(구매자2_기존_평가_3개_새로운_평가_2개) + .score(new Score(구매자2_기존_신뢰도_점수)) + .build(); + final Review 구매자2_기존_평가2 = Review.builder() + .auction(경매4) + .writer(판매자) + .target(구매자2_기존_평가_3개_새로운_평가_2개) + .score(new Score(구매자2_기존_신뢰도_점수)) + .build(); + final Review 구매자2_기존_평가3 = Review.builder() + .auction(경매5) + .writer(판매자) + .target(구매자2_기존_평가_3개_새로운_평가_2개) + .score(new Score(구매자2_기존_신뢰도_점수)) + .build(); + final Review 구매자3_기존_평가1 = Review.builder() + .auction(경매6) + .writer(판매자) + .target(구매자3_기존_평가_5개_새로운_평가_1개) + .score(new Score(구매자3_기존_신뢰도_점수)) + .build(); + final Review 구매자3_기존_평가2 = Review.builder() + .auction(경매7) + .writer(판매자) + .target(구매자3_기존_평가_5개_새로운_평가_1개) + .score(new Score(구매자3_기존_신뢰도_점수)) + .build(); + final Review 구매자3_기존_평가3 = Review.builder() + .auction(경매8) + .writer(판매자) + .target(구매자3_기존_평가_5개_새로운_평가_1개) + .score(new Score(구매자3_기존_신뢰도_점수)) + .build(); + final Review 구매자3_기존_평가4 = Review.builder() + .auction(경매9) + .writer(판매자) + .target(구매자3_기존_평가_5개_새로운_평가_1개) + .score(new Score(구매자3_기존_신뢰도_점수)) + .build(); + final Review 구매자3_기존_평가5 = Review.builder() + .auction(경매10) + .writer(판매자) + .target(구매자3_기존_평가_5개_새로운_평가_1개) + .score(new Score(구매자3_기존_신뢰도_점수)) + .build(); + + reviewRepository.save(구매자1_기존_평가1); + reviewRepository.save(구매자1_기존_평가2); + reviewRepository.save(구매자2_기존_평가1); + reviewRepository.save(구매자2_기존_평가2); + reviewRepository.save(구매자2_기존_평가3); + reviewRepository.save(구매자3_기존_평가1); + reviewRepository.save(구매자3_기존_평가2); + reviewRepository.save(구매자3_기존_평가3); + reviewRepository.save(구매자3_기존_평가4); + reviewRepository.save(구매자3_기존_평가5); + + final UserReliability 구매자1_기존_신뢰도_반영_정보 = new UserReliability(구매자1_기존_평가_2개_새로운_평가_1개); + 구매자1_기존_신뢰도_반영_정보.updateReliability(new Reviews(List.of(구매자1_기존_평가1, 구매자1_기존_평가2))); + + final UserReliability 구매자2_기존_신뢰도_반영_정보 = new UserReliability(구매자2_기존_평가_3개_새로운_평가_2개); + 구매자2_기존_신뢰도_반영_정보.updateReliability(new Reviews(List.of(구매자2_기존_평가1, 구매자2_기존_평가2, 구매자2_기존_평가3))); + + final UserReliability 구매자3_기존_신뢰도_반영_정보 = new UserReliability(구매자3_기존_평가_5개_새로운_평가_1개); + 구매자3_기존_신뢰도_반영_정보.updateReliability(new Reviews(List.of(구매자3_기존_평가1, 구매자3_기존_평가2, 구매자3_기존_평가3, 구매자3_기존_평가4, 구매자3_기존_평가5))); + + userReliabilityRepository.save(구매자1_기존_신뢰도_반영_정보); + userReliabilityRepository.save(구매자2_기존_신뢰도_반영_정보); + userReliabilityRepository.save(구매자3_기존_신뢰도_반영_정보); + + reliabilityUpdateHistoryRepository.save(new ReliabilityUpdateHistory(기존에_적용된_평가_중_마지막_평가의_아이디)); + + 구매자1_새로운_평가1 = Review.builder() + .auction(경매11) + .writer(판매자) + .target(구매자1_기존_평가_2개_새로운_평가_1개) + .score(new Score(구매자1_새로운_평가1_점수)) + .build(); + 구매자2_새로운_평가1 = Review.builder() + .auction(경매12) + .writer(판매자) + .target(구매자2_기존_평가_3개_새로운_평가_2개) + .score(new Score(구매자2_새로운_평가1_점수)) + .build(); + 구매자2_새로운_평가2 = Review.builder() + .auction(경매13) + .writer(판매자) + .target(구매자2_기존_평가_3개_새로운_평가_2개) + .score(new Score(구매자2_새로운_평가2_점수)) + .build(); + 구매자3_새로운_평가1 = Review.builder() + .auction(경매14) + .writer(판매자) + .target(구매자3_기존_평가_5개_새로운_평가_1개) + .score(new Score(구매자3_새로운_평가1_점수)) + .build(); + 구매자4_새로운_평가1 = Review.builder() + .auction(경매15) + .writer(판매자) + .target(구매자4_기존_평가_없고_새로운_평가_1개) + .score(new Score(구매자4_새로운_평가1_점수)) + .build(); + 구매자5_새로운_평가1 = Review.builder() + .auction(경매16) + .writer(판매자) + .target(구매자5_기존_평가_없고_새로운_평가_3개) + .score(new Score(구매자5_새로운_평가1_점수)) + .build(); + 구매자5_새로운_평가2 = Review.builder() + .auction(경매17) + .writer(판매자) + .target(구매자5_기존_평가_없고_새로운_평가_3개) + .score(new Score(구매자5_새로운_평가2_점수)) + .build(); + 구매자5_새로운_평가3 = Review.builder() + .auction(경매18) + .writer(판매자) + .target(구매자5_기존_평가_없고_새로운_평가_3개) + .score(new Score(구매자5_새로운_평가3_점수)) + .build(); + + reviewRepository.save(구매자1_새로운_평가1); + reviewRepository.save(구매자2_새로운_평가1); + reviewRepository.save(구매자2_새로운_평가2); + reviewRepository.save(구매자3_새로운_평가1); + reviewRepository.save(구매자4_새로운_평가1); + reviewRepository.save(구매자5_새로운_평가1); + reviewRepository.save(구매자5_새로운_평가2); + reviewRepository.save(구매자5_새로운_평가3); + } +} diff --git a/backend/ddang/src/test/java/com/ddang/ddang/user/domain/ReliabilityTest.java b/backend/ddang/src/test/java/com/ddang/ddang/user/domain/ReliabilityTest.java index 9aec3fc19..0fd14c11f 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/user/domain/ReliabilityTest.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/user/domain/ReliabilityTest.java @@ -1,38 +1,28 @@ package com.ddang.ddang.user.domain; -import com.ddang.ddang.review.domain.Review; -import com.ddang.ddang.user.domain.fixture.ReliabilityFixture; import org.junit.jupiter.api.DisplayNameGeneration; import org.junit.jupiter.api.DisplayNameGenerator; import org.junit.jupiter.api.Test; -import java.util.Collections; -import java.util.List; - import static org.assertj.core.api.Assertions.assertThat; @DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) @SuppressWarnings("NonAsciiCharacters") -class ReliabilityTest extends ReliabilityFixture { - - @Test - void 신뢰도_점수_평균을_계산한다() { - // when - 평가_대상.updateReliability(평가_대상이_받은_평가_목록); - - // then - assertThat(평가_대상.getReliability().getValue()).isEqualTo(평가_대상의_신뢰도_점수); - } +class ReliabilityTest { @Test - void 신뢰도_기록이_없다면_신뢰도_점수는_null이다() { + void 신뢰도에_반영된_평가의_개수가_주어지면_신뢰도에_반영된_평가_점수들의_합을_반환한다() { // given - final List targetReviews = Collections.emptyList(); + final double reliabilityValue = 4.0d; + final Reliability reliability = new Reliability(reliabilityValue); + final int appliedReviewCount = 3; + + final double expect = reliabilityValue * appliedReviewCount; // when - 평가_대상.updateReliability(targetReviews); + final double actual = reliability.calculateReviewScoreSum(appliedReviewCount); // then - assertThat(평가_대상.getReliability().getValue()).isNull(); + assertThat(actual).isEqualTo(expect); } } diff --git a/backend/ddang/src/test/java/com/ddang/ddang/user/domain/ReliabilityUpdateHistoryTest.java b/backend/ddang/src/test/java/com/ddang/ddang/user/domain/ReliabilityUpdateHistoryTest.java new file mode 100644 index 000000000..563981a7c --- /dev/null +++ b/backend/ddang/src/test/java/com/ddang/ddang/user/domain/ReliabilityUpdateHistoryTest.java @@ -0,0 +1,32 @@ +package com.ddang.ddang.user.domain; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class ReliabilityUpdateHistoryTest { + + @Test + void 빈_생성자로_생성하면_마지막으로_적용된_평가_아이디가_0이다() { + // given + final Long expect = 0L; + + // when + final ReliabilityUpdateHistory actual = new ReliabilityUpdateHistory(); + + // then + assertThat(actual.getLastAppliedReviewId()).isEqualTo(expect); + } + + @Test + void 생성자에_마직막으로_적용된_평가_아이디를_지정할_수_있다() { + // given + final Long lastAppliedReviewId = 2L; + + // when + final ReliabilityUpdateHistory actual = new ReliabilityUpdateHistory(2L); + + // then + assertThat(actual.getLastAppliedReviewId()).isEqualTo(lastAppliedReviewId); + } +} diff --git a/backend/ddang/src/test/java/com/ddang/ddang/user/domain/UserReliabilityTest.java b/backend/ddang/src/test/java/com/ddang/ddang/user/domain/UserReliabilityTest.java new file mode 100644 index 000000000..c2f274dd8 --- /dev/null +++ b/backend/ddang/src/test/java/com/ddang/ddang/user/domain/UserReliabilityTest.java @@ -0,0 +1,44 @@ +package com.ddang.ddang.user.domain; + +import com.ddang.ddang.user.domain.fixture.UserReliabilityFixture; +import org.assertj.core.api.SoftAssertions; +import org.junit.jupiter.api.Test; +import org.springframework.test.util.ReflectionTestUtils; + +class UserReliabilityTest extends UserReliabilityFixture { + + @Test + void 새로운_평가가_주어지면_사용자의_신뢰도_관련_정보들을_갱신한다() { + // given + final UserReliability userReliability = new UserReliability(기존에_평가_3개_받은_평가_대상); + ReflectionTestUtils.setField(userReliability, "appliedReviewCount", 3); + + // when + userReliability.updateReliability(평가_대상이_받은_새로운_평가_목록); + + // then + SoftAssertions.assertSoftly(softAssertions -> { + softAssertions.assertThat(userReliability.getReliability()).isEqualTo(새로운_평가가_반영된_신뢰도); + softAssertions.assertThat(userReliability.getAppliedReviewCount()).isEqualTo(새로운_평가_반영_이후의_평가_대상의_신뢰도에_반영된_평가_개수); + softAssertions.assertThat(기존에_평가_3개_받은_평가_대상.getReliability()).isEqualTo(새로운_평가가_반영된_신뢰도); + }); + } + + @Test + void 신뢰도_기록이_없다면_신뢰도_점수는_그대로이다() { + // given + final UserReliability userReliability = new UserReliability(기존에_평가_3개_받은_평가_대상); + ReflectionTestUtils.setField(userReliability, "appliedReviewCount", 3); + + // when + userReliability.updateReliability(비어있는_평가_목록); + + // then + + SoftAssertions.assertSoftly(softAssertions -> { + softAssertions.assertThat(userReliability.getReliability()).isEqualTo(기존에_평가3개가_적용되어있는_신뢰도); + softAssertions.assertThat(userReliability.getAppliedReviewCount()).isEqualTo(새로운_평가_반영_전의_평가_대상의_신뢰도에_반영된_평가_개수); + softAssertions.assertThat(기존에_평가_3개_받은_평가_대상.getReliability()).isEqualTo(기존에_평가3개가_적용되어있는_신뢰도); + }); + } +} diff --git a/backend/ddang/src/test/java/com/ddang/ddang/user/domain/UserTest.java b/backend/ddang/src/test/java/com/ddang/ddang/user/domain/UserTest.java index 9946a3d3b..aea916432 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/user/domain/UserTest.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/user/domain/UserTest.java @@ -14,18 +14,18 @@ class UserTest extends UserFixture { @Test - void 회원_생성시_신뢰도가_null이면_신뢰도_점수가_null인_신뢰도로_회원을_생성한다() { + void 회원_생성시_신뢰도가_null이면_신뢰도_점수가_double의_최솟값인_신뢰도로_회원을_생성한다() { // given final Reliability nullReliability = null; - final Reliability expect = new Reliability(null); + final Reliability expect = new Reliability(Double.MIN_VALUE); // when final User user = User.builder() - .name("kakao12345") - .profileImage(new ProfileImage("uplad.png", "store.png")) - .reliability(nullReliability) - .oauthId("12345") - .build(); + .name("kakao12345") + .profileImage(new ProfileImage("uplad.png", "store.png")) + .reliability(nullReliability) + .oauthId("12345") + .build(); // then assertThat(user.getReliability()).isEqualTo(expect); @@ -93,8 +93,9 @@ class UserTest extends UserFixture { @Test void 회원_탈퇴한다() { // given + final String userName = "kakao12345"; final User user = User.builder() - .name("kakao12345") + .name(userName) .profileImage(new ProfileImage("upload.png", "store.png")) .reliability(new Reliability(5.0d)) .oauthId("12345") @@ -104,15 +105,19 @@ class UserTest extends UserFixture { user.withdrawal(); // then - assertThat(user.isDeleted()).isTrue(); + SoftAssertions.assertSoftly(softAssertions -> { + softAssertions.assertThat(user.isDeleted()).isTrue(); + softAssertions.assertThat(user.getName()).isNotEqualTo(userName); + softAssertions.assertThat(user.getProfileImage()).isNull(); + }); } @Test - void 신뢰도_평균을_계산한다() { + void 새로운_신뢰도로_변경한다() { // when - 평가_대상.updateReliability(평가_대상이_받은_평가_목록); + 평가_대상.updateReliability(새로운_신뢰도); // then - assertThat(평가_대상.getReliability().getValue()).isEqualTo(평가_대상의_신뢰도_점수); + assertThat(평가_대상.getReliability().getValue()).isEqualTo(새로운_신뢰도_점수); } } diff --git a/backend/ddang/src/test/java/com/ddang/ddang/user/domain/fixture/ReliabilityFixture.java b/backend/ddang/src/test/java/com/ddang/ddang/user/domain/fixture/ReliabilityFixture.java deleted file mode 100644 index bd0ff2e2d..000000000 --- a/backend/ddang/src/test/java/com/ddang/ddang/user/domain/fixture/ReliabilityFixture.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.ddang.ddang.user.domain.fixture; - -import com.ddang.ddang.image.domain.ProfileImage; -import com.ddang.ddang.review.domain.Review; -import com.ddang.ddang.review.domain.Score; -import com.ddang.ddang.user.domain.Reliability; -import com.ddang.ddang.user.domain.User; - -import java.util.List; - -@SuppressWarnings("NonAsciiCharacters") -public class ReliabilityFixture { - - private double 평가1_점수 = 5.0d; - private double 평가2_점수 = 1.0d; - private User 평가_작성자1 = User.builder() - .name("평가 작성자1") - .profileImage(new ProfileImage("profile.png", "profile.png")) - .reliability(new Reliability(4.7d)) - .oauthId("12346") - .build(); - private User 평가_작성자2 = User.builder() - .name("평가 작성자2") - .profileImage(new ProfileImage("profile.png", "profile.png")) - .reliability(new Reliability(4.7d)) - .oauthId("12347") - .build(); - - protected User 평가_대상 = User.builder() - .name("평가 대상") - .profileImage(new ProfileImage("profile.png", "profile.png")) - .reliability(new Reliability(null)) - .oauthId("12345") - .build(); - private Review 평가1 = Review.builder() - .writer(평가_작성자1) - .target(평가_대상) - .content("친절하다") - .score(new Score(평가1_점수)) - .build(); - private Review 평가2 = Review.builder() - .writer(평가_작성자2) - .target(평가_대상) - .content("별로다") - .score(new Score(평가2_점수)) - .build(); - - protected List 평가_대상이_받은_평가_목록 = List.of(평가1, 평가2); - protected Double 평가_대상의_신뢰도_점수 = (평가1_점수 + 평가2_점수) / 2; -} diff --git a/backend/ddang/src/test/java/com/ddang/ddang/user/domain/fixture/UserFixture.java b/backend/ddang/src/test/java/com/ddang/ddang/user/domain/fixture/UserFixture.java index 44c558433..71c9f2949 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/user/domain/fixture/UserFixture.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/user/domain/fixture/UserFixture.java @@ -1,50 +1,26 @@ package com.ddang.ddang.user.domain.fixture; import com.ddang.ddang.image.domain.ProfileImage; -import com.ddang.ddang.review.domain.Review; -import com.ddang.ddang.review.domain.Score; import com.ddang.ddang.user.domain.Reliability; import com.ddang.ddang.user.domain.User; - -import java.util.List; +import org.junit.jupiter.api.BeforeEach; @SuppressWarnings("NonAsciiCharacters") public class UserFixture { - private double 평가1_점수 = 5.0d; - private double 평가2_점수 = 1.0d; - private User 평가_작성자1 = User.builder() - .name("평가 작성자1") - .profileImage(new ProfileImage("profile.png", "profile.png")) - .reliability(new Reliability(4.7d)) - .oauthId("12346") - .build(); - private User 평가_작성자2 = User.builder() - .name("평가 작성자2") - .profileImage(new ProfileImage("profile.png", "profile.png")) - .reliability(new Reliability(4.7d)) - .oauthId("12347") - .build(); + protected double 새로운_신뢰도_점수 = 3.0d; + protected User 평가_대상; + protected Reliability 새로운_신뢰도; - protected User 평가_대상 = User.builder() - .name("평가 대상") - .profileImage(new ProfileImage("profile.png", "profile.png")) - .reliability(new Reliability(null)) - .oauthId("12345") - .build(); - private Review 평가1 = Review.builder() - .writer(평가_작성자1) - .target(평가_대상) - .content("친절하다") - .score(new Score(평가1_점수)) - .build(); - private Review 평가2 = Review.builder() - .writer(평가_작성자2) - .target(평가_대상) - .content("별로다") - .score(new Score(평가2_점수)) - .build(); + @BeforeEach + void setUp() { + 평가_대상 = User.builder() + .name("평가 대상") + .profileImage(new ProfileImage("profile.png", "profile.png")) + .reliability(new Reliability(0.0d)) + .oauthId("12345") + .build(); - protected List 평가_대상이_받은_평가_목록 = List.of(평가1, 평가2); - protected Double 평가_대상의_신뢰도_점수 = (평가1_점수 + 평가2_점수) / 2; + 새로운_신뢰도 = new Reliability(새로운_신뢰도_점수); + } } diff --git a/backend/ddang/src/test/java/com/ddang/ddang/user/domain/fixture/UserReliabilityFixture.java b/backend/ddang/src/test/java/com/ddang/ddang/user/domain/fixture/UserReliabilityFixture.java new file mode 100644 index 000000000..8b244dd68 --- /dev/null +++ b/backend/ddang/src/test/java/com/ddang/ddang/user/domain/fixture/UserReliabilityFixture.java @@ -0,0 +1,75 @@ +package com.ddang.ddang.user.domain.fixture; + +import com.ddang.ddang.image.domain.ProfileImage; +import com.ddang.ddang.review.domain.Review; +import com.ddang.ddang.review.domain.Reviews; +import com.ddang.ddang.review.domain.Score; +import com.ddang.ddang.user.domain.Reliability; +import com.ddang.ddang.user.domain.User; +import org.junit.jupiter.api.BeforeEach; + +import java.util.Collections; +import java.util.List; + +@SuppressWarnings("NonAsciiCharacters") +public class UserReliabilityFixture { + + private double 새로운_평가1_점수 = 5.0d; + private double 새로운_평가2_점수 = 1.0d; + private double 새로운_평가_반영_전의_평가_대상의_신뢰도_점수 = 4.0d; + protected int 새로운_평가_반영_전의_평가_대상의_신뢰도에_반영된_평가_개수 = 3; + protected int 새로운_평가_반영_이후의_평가_대상의_신뢰도에_반영된_평가_개수 = 새로운_평가_반영_전의_평가_대상의_신뢰도에_반영된_평가_개수 + 2; + protected double 새로운_평가_반영_이후의_평가_대상의_신뢰도_점수 = + (새로운_평가_반영_전의_평가_대상의_신뢰도_점수 * 새로운_평가_반영_전의_평가_대상의_신뢰도에_반영된_평가_개수 + + 새로운_평가1_점수 + 새로운_평가2_점수) / 새로운_평가_반영_이후의_평가_대상의_신뢰도에_반영된_평가_개수; + protected User 기존에_평가_3개_받은_평가_대상; + protected Reviews 평가_대상이_받은_새로운_평가_목록; + protected Reviews 비어있는_평가_목록; + protected Reliability 기존에_평가3개가_적용되어있는_신뢰도; + protected Reliability 새로운_평가가_반영된_신뢰도; + + @BeforeEach + void setUp() { + final User 평가_작성자1 = User.builder() + .name("평가 작성자1") + .profileImage(new ProfileImage("profile.png", "profile.png")) + .reliability(new Reliability(4.7d)) + .oauthId("12346") + .build(); + final User 평가_작성자2 = User.builder() + .name("평가 작성자2") + .profileImage(new ProfileImage("profile.png", "profile.png")) + .reliability(new Reliability(4.7d)) + .oauthId("12347") + .build(); + + + 기존에_평가3개가_적용되어있는_신뢰도 = new Reliability(새로운_평가_반영_전의_평가_대상의_신뢰도_점수); + + 기존에_평가_3개_받은_평가_대상 = User.builder() + .name("평가 대상") + .profileImage(new ProfileImage("profile.png", "profile.png")) + .reliability(기존에_평가3개가_적용되어있는_신뢰도) + .oauthId("12345") + .build(); + + final Review 새로운_평가1 = Review.builder() + .writer(평가_작성자1) + .target(기존에_평가_3개_받은_평가_대상) + .content("친절하다") + .score(new Score(새로운_평가1_점수)) + .build(); + final Review 새로운_평가2 = Review.builder() + .writer(평가_작성자2) + .target(기존에_평가_3개_받은_평가_대상) + .content("별로다") + .score(new Score(새로운_평가2_점수)) + .build(); + + 평가_대상이_받은_새로운_평가_목록 = new Reviews(List.of(새로운_평가1, 새로운_평가2)); + + 비어있는_평가_목록 = new Reviews(Collections.emptyList()); + + 새로운_평가가_반영된_신뢰도 = new Reliability(새로운_평가_반영_이후의_평가_대상의_신뢰도_점수); + } +} diff --git a/backend/ddang/src/test/java/com/ddang/ddang/user/infrastructure/persistence/JpaReliabilityUpdateHistoryRepositoryTest.java b/backend/ddang/src/test/java/com/ddang/ddang/user/infrastructure/persistence/JpaReliabilityUpdateHistoryRepositoryTest.java new file mode 100644 index 000000000..3e365aba4 --- /dev/null +++ b/backend/ddang/src/test/java/com/ddang/ddang/user/infrastructure/persistence/JpaReliabilityUpdateHistoryRepositoryTest.java @@ -0,0 +1,63 @@ +package com.ddang.ddang.user.infrastructure.persistence; + +import com.ddang.ddang.configuration.JpaConfiguration; +import com.ddang.ddang.configuration.QuerydslConfiguration; +import com.ddang.ddang.user.domain.ReliabilityUpdateHistory; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.context.annotation.Import; + +import java.util.List; +import java.util.Optional; + +import static org.assertj.core.api.Assertions.assertThat; + +@DataJpaTest +@Import({JpaConfiguration.class, QuerydslConfiguration.class}) +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) +@SuppressWarnings("NonAsciiCharacters") +class JpaReliabilityUpdateHistoryRepositoryTest { + + @Autowired + JpaReliabilityUpdateHistoryRepository reliabilityUpdateHistoryRepository; + + @Test + void 신뢰도_업데이트_기록을_저장한다() { + // given + final ReliabilityUpdateHistory reliabilityUpdateHistory = new ReliabilityUpdateHistory(); + + // when + final ReliabilityUpdateHistory actual = reliabilityUpdateHistoryRepository.save(reliabilityUpdateHistory); + + // then + assertThat(actual.getId()).isPositive(); + } + + @Test + void 신뢰도_업데이트_기록이_없으면_빈_Optional을_반환한다() { + // when + final Optional actual = reliabilityUpdateHistoryRepository.findFirstByOrderByIdDesc(); + + // then + assertThat(actual).isEmpty(); + } + + @Test + void 신뢰도_업데이트_기록의_아이디가_가장_큰_것을_조회한다() { + // given + final ReliabilityUpdateHistory history1 = new ReliabilityUpdateHistory(); + final ReliabilityUpdateHistory history2 = new ReliabilityUpdateHistory(); + final ReliabilityUpdateHistory history3 = new ReliabilityUpdateHistory(); + + reliabilityUpdateHistoryRepository.saveAll(List.of(history1, history2, history3)); + + // when + final Optional actual = reliabilityUpdateHistoryRepository.findFirstByOrderByIdDesc(); + + // then + assertThat(actual).contains(history3); + } +} diff --git a/backend/ddang/src/test/java/com/ddang/ddang/user/infrastructure/persistence/JpaUserReliabilityRepositoryTest.java b/backend/ddang/src/test/java/com/ddang/ddang/user/infrastructure/persistence/JpaUserReliabilityRepositoryTest.java new file mode 100644 index 000000000..ba2f5aae1 --- /dev/null +++ b/backend/ddang/src/test/java/com/ddang/ddang/user/infrastructure/persistence/JpaUserReliabilityRepositoryTest.java @@ -0,0 +1,44 @@ +package com.ddang.ddang.user.infrastructure.persistence; + +import com.ddang.ddang.configuration.JpaConfiguration; +import com.ddang.ddang.configuration.QuerydslConfiguration; +import com.ddang.ddang.user.domain.UserReliability; +import com.ddang.ddang.user.infrastructure.persistence.fixture.JpaUserReliabilityRepositoryFixture; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.context.annotation.Import; + +import java.util.Optional; + +import static org.assertj.core.api.Assertions.assertThat; + +@DataJpaTest +@Import({JpaConfiguration.class, QuerydslConfiguration.class}) +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) +@SuppressWarnings("NonAsciiCharacters") +class JpaUserReliabilityRepositoryTest extends JpaUserReliabilityRepositoryFixture { + + @Autowired + JpaUserReliabilityRepository userReliabilityRepository; + + @Test + void 사용자_신뢰도_정보를_저장한다() { + // when + UserReliability actual = userReliabilityRepository.save(저장하기_전_사용자_신뢰도_엔티티); + + // then + assertThat(actual.getId()).isPositive(); + } + + @Test + void 주어진_사용자_아이디에_해당하는_사용자_신뢰도_정보를_반환한다() { + // when + final Optional actual = userReliabilityRepository.findByUserId(사용자.getId()); + + // then + assertThat(actual).contains(사용자_신뢰도_정보); + } +} diff --git a/backend/ddang/src/test/java/com/ddang/ddang/user/infrastructure/persistence/JpaUserRepositoryTest.java b/backend/ddang/src/test/java/com/ddang/ddang/user/infrastructure/persistence/JpaUserRepositoryTest.java index c6564ec79..d907a04fd 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/user/infrastructure/persistence/JpaUserRepositoryTest.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/user/infrastructure/persistence/JpaUserRepositoryTest.java @@ -5,8 +5,6 @@ import com.ddang.ddang.user.domain.Reliability; import com.ddang.ddang.user.domain.User; import com.ddang.ddang.user.infrastructure.persistence.fixture.JpaUserRepositoryFixture; -import jakarta.persistence.EntityManager; -import jakarta.persistence.PersistenceContext; import org.junit.jupiter.api.DisplayNameGeneration; import org.junit.jupiter.api.DisplayNameGenerator; import org.junit.jupiter.api.Test; @@ -24,9 +22,6 @@ @SuppressWarnings("NonAsciiCharacters") class JpaUserRepositoryTest extends JpaUserRepositoryFixture { - @PersistenceContext - EntityManager em; - @Autowired JpaUserRepository userRepository; @@ -44,59 +39,56 @@ class JpaUserRepositoryTest extends JpaUserRepositoryFixture { final User actual = userRepository.save(user); // then - em.flush(); - em.clear(); - assertThat(actual.getId()).isPositive(); } @Test - void 존재하는_oauthId를_전달하면_해당_회원을_Optional로_감싸_반환한다() { + void 존재하는_사용자_아이디를_전달하면_해당_사용자를_Optional로_감싸_반환한다() { // when - final Optional actual = userRepository.findByOauthIdAndDeletedIsFalse(사용자.getOauthId()); + final Optional actual = userRepository.findById(사용자.getId()); // then assertThat(actual).contains(사용자); } @Test - void 존재하지_않는_oauthId를_전달하면_해당_회원을_빈_Optional로_반환한다() { + void 존재하지_않는_사용자_아이디를_전달하면_빈_Optional을_반환한다() { // when - final Optional actual = userRepository.findByOauthIdAndDeletedIsFalse(존재하지_않는_oauth_아이디); + final Optional actual = userRepository.findById(존재하지_않는_사용자_아이디); // then assertThat(actual).isEmpty(); } @Test - void 회원가입과_탈퇴하지_않은_회원_id를_전달하면_해당_회원을_Optional로_감싸_반환한다() { + void 회원탈퇴한_사용자의_id를_전달하면_빈_Optional을_반환한다() { // when - final Optional actual = userRepository.findByIdAndDeletedIsFalse(사용자.getId()); + final Optional actual = userRepository.findById(탈퇴한_사용자.getId()); // then - assertThat(actual).contains(사용자); + assertThat(actual).isEmpty(); } @Test - void 회원탈퇴한_회원의_id를_전달하면_빈_Optional을_반환한다() { + void 존재하는_oauthId를_전달하면_해당_사용자를_Optional로_감싸_반환한다() { // when - final Optional actual = userRepository.findByIdAndDeletedIsFalse(탈퇴한_사용자.getId()); + final Optional actual = userRepository.findByOauthId(사용자.getOauthInformation().getOauthId()); // then - assertThat(actual).isEmpty(); + assertThat(actual).contains(사용자); } @Test - void 없는_id를_전달하면_빈_Optional을_반환한다() { + void 존재하지_않는_oauthId를_전달하면_해당_사용자를_빈_Optional로_반환한다() { // when - final Optional actual = userRepository.findByIdAndDeletedIsFalse(존재하지_않는_사용자_아이디); + final Optional actual = userRepository.findByOauthId(존재하지_않는_oauth_아이디); // then assertThat(actual).isEmpty(); } @Test - void 회원탈퇴한_회원의_id를_전달하면_참을_반환한다() { + void 회원탈퇴한_사용자의_id를_전달하면_참을_반환한다() { // when final boolean actual = userRepository.existsByIdAndDeletedIsTrue(탈퇴한_사용자.getId()); @@ -105,7 +97,7 @@ class JpaUserRepositoryTest extends JpaUserRepositoryFixture { } @Test - void 회원탈퇴하지_않거나_회원가입하지_않은_회원의_id를_전달하면_거짓을_반환한다() { + void 회원탈퇴하지_않거나_회원가입하지_않은_사용자의_id를_전달하면_거짓을_반환한다() { // when final boolean actual = userRepository.existsByIdAndDeletedIsTrue(사용자.getId()); @@ -114,7 +106,7 @@ class JpaUserRepositoryTest extends JpaUserRepositoryFixture { } @Test - void 이름이_아직_없다면_거짓을_반환한다() { + void 마지막_이름이_동일한_이름이_아직_없다면_거짓을_반환한다() { // when final boolean actual = userRepository.existsByNameEndingWith(존재하지_않는_사용자_이름); @@ -122,12 +114,30 @@ class JpaUserRepositoryTest extends JpaUserRepositoryFixture { assertThat(actual).isFalse(); } + @Test + void 마지막_이름이_동일한_이름이_있다면_참을_반환한다() { + // when + final boolean actual = userRepository.existsByNameEndingWith(끝이_동일한_이름); + + // then + assertThat(actual).isTrue(); + } + @Test void 이름이_있다면_참을_반환한다() { // when - final boolean actual = userRepository.existsByNameEndingWith(존재하는_사용자_이름); + final boolean actual = userRepository.existsByName(존재하는_사용자_이름); // then assertThat(actual).isTrue(); } + + @Test + void 이름이_없다면_거짓을_반환한다() { + // when + final boolean actual = userRepository.existsByName(존재하지_않는_사용자_이름); + + // then + assertThat(actual).isFalse(); + } } diff --git a/backend/ddang/src/test/java/com/ddang/ddang/user/infrastructure/persistence/ReliabilityUpdateHistoryRepositoryImplTest.java b/backend/ddang/src/test/java/com/ddang/ddang/user/infrastructure/persistence/ReliabilityUpdateHistoryRepositoryImplTest.java new file mode 100644 index 000000000..30a854a67 --- /dev/null +++ b/backend/ddang/src/test/java/com/ddang/ddang/user/infrastructure/persistence/ReliabilityUpdateHistoryRepositoryImplTest.java @@ -0,0 +1,70 @@ +package com.ddang.ddang.user.infrastructure.persistence; + +import com.ddang.ddang.configuration.JpaConfiguration; +import com.ddang.ddang.configuration.QuerydslConfiguration; +import com.ddang.ddang.user.domain.ReliabilityUpdateHistory; +import com.ddang.ddang.user.domain.repository.ReliabilityUpdateHistoryRepository; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.context.annotation.Import; + +import java.util.Optional; + +import static org.assertj.core.api.Assertions.assertThat; + +@DataJpaTest +@Import({JpaConfiguration.class, QuerydslConfiguration.class}) +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) +@SuppressWarnings("NonAsciiCharacters") +class ReliabilityUpdateHistoryRepositoryImplTest { + + ReliabilityUpdateHistoryRepository reliabilityUpdateHistoryRepository; + + @BeforeEach + void setUp(@Autowired final JpaReliabilityUpdateHistoryRepository jpaReliabilityUpdateHistoryRepository) { + reliabilityUpdateHistoryRepository = new ReliabilityUpdateHistoryRepositoryImpl(jpaReliabilityUpdateHistoryRepository); + } + + @Test + void 신뢰도_업데이트_기록을_저장한다() { + // given + final ReliabilityUpdateHistory reliabilityUpdateHistory = new ReliabilityUpdateHistory(); + + // when + final ReliabilityUpdateHistory actual = reliabilityUpdateHistoryRepository.save(reliabilityUpdateHistory); + + // then + assertThat(actual.getId()).isPositive(); + } + + @Test + void 신뢰도_업데이트_기록이_없으면_빈_Optional을_반환한다() { + // when + final Optional actual = reliabilityUpdateHistoryRepository.findFirstByOrderByIdDesc(); + + // then + assertThat(actual).isEmpty(); + } + + @Test + void 신뢰도_업데이트_기록의_아이디가_가장_큰_것을_조회한다() { + // given + final ReliabilityUpdateHistory history1 = new ReliabilityUpdateHistory(); + final ReliabilityUpdateHistory history2 = new ReliabilityUpdateHistory(); + final ReliabilityUpdateHistory history3 = new ReliabilityUpdateHistory(); + + reliabilityUpdateHistoryRepository.save(history1); + reliabilityUpdateHistoryRepository.save(history2); + reliabilityUpdateHistoryRepository.save(history3); + + // when + final Optional actual = reliabilityUpdateHistoryRepository.findFirstByOrderByIdDesc(); + + // then + assertThat(actual).contains(history3); + } +} diff --git a/backend/ddang/src/test/java/com/ddang/ddang/user/infrastructure/persistence/UserReliabilityRepositoryImplTest.java b/backend/ddang/src/test/java/com/ddang/ddang/user/infrastructure/persistence/UserReliabilityRepositoryImplTest.java new file mode 100644 index 000000000..99bb6fc83 --- /dev/null +++ b/backend/ddang/src/test/java/com/ddang/ddang/user/infrastructure/persistence/UserReliabilityRepositoryImplTest.java @@ -0,0 +1,50 @@ +package com.ddang.ddang.user.infrastructure.persistence; + +import com.ddang.ddang.configuration.JpaConfiguration; +import com.ddang.ddang.configuration.QuerydslConfiguration; +import com.ddang.ddang.user.domain.UserReliability; +import com.ddang.ddang.user.domain.repository.UserReliabilityRepository; +import com.ddang.ddang.user.infrastructure.persistence.fixture.UserReliabilityRepositoryImplFixture; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.context.annotation.Import; + +import java.util.Optional; + +import static org.assertj.core.api.Assertions.assertThat; + +@DataJpaTest +@Import({JpaConfiguration.class, QuerydslConfiguration.class}) +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) +@SuppressWarnings("NonAsciiCharacters") +class UserReliabilityRepositoryImplTest extends UserReliabilityRepositoryImplFixture { + + UserReliabilityRepository userReliabilityRepository; + + @BeforeEach + void setUp(@Autowired final JpaUserReliabilityRepository jpaUserReliabilityRepository) { + userReliabilityRepository = new UserReliabilityRepositoryImpl(jpaUserReliabilityRepository); + } + + @Test + void 사용자_신뢰도_정보를_저장한다() { + // when + final UserReliability actual = userReliabilityRepository.save(저장하기_전_사용자_신뢰도_엔티티); + + // then + assertThat(actual.getId()).isPositive(); + } + + @Test + void 주어진_사용자_아이디에_해당하는_사용자_신뢰도_정보를_반환한다() { + // when + final Optional actual = userReliabilityRepository.findByUserId(사용자.getId()); + + // then + assertThat(actual).contains(사용자_신뢰도_정보); + } +} diff --git a/backend/ddang/src/test/java/com/ddang/ddang/user/infrastructure/persistence/UserRepositoryImplTest.java b/backend/ddang/src/test/java/com/ddang/ddang/user/infrastructure/persistence/UserRepositoryImplTest.java new file mode 100644 index 000000000..775126096 --- /dev/null +++ b/backend/ddang/src/test/java/com/ddang/ddang/user/infrastructure/persistence/UserRepositoryImplTest.java @@ -0,0 +1,149 @@ +package com.ddang.ddang.user.infrastructure.persistence; + +import com.ddang.ddang.configuration.JpaConfiguration; +import com.ddang.ddang.configuration.QuerydslConfiguration; +import com.ddang.ddang.user.domain.Reliability; +import com.ddang.ddang.user.domain.User; +import com.ddang.ddang.user.domain.repository.UserRepository; +import com.ddang.ddang.user.infrastructure.persistence.fixture.UserRepositoryImplFixture; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.context.annotation.Import; + +import java.util.Optional; + +import static org.assertj.core.api.Assertions.assertThat; + +@DataJpaTest +@Import({JpaConfiguration.class, QuerydslConfiguration.class}) +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) +@SuppressWarnings("NonAsciiCharacters") +class UserRepositoryImplTest extends UserRepositoryImplFixture { + + UserRepository userRepository; + + @BeforeEach + void setUp(@Autowired final JpaUserRepository jpaUserRepository) { + userRepository = new UserRepositoryImpl(jpaUserRepository); + } + + @Test + void 사용자를_저장한다() { + // given + final User user = User.builder() + .name("새로운 사용자") + .profileImage(프로필_이미지) + .reliability(new Reliability(4.7d)) + .oauthId("12345") + .build(); + + // when + final User actual = userRepository.save(user); + + // then + assertThat(actual.getId()).isPositive(); + } + + @Test + void 존재하는_사용자_아이디를_전달하면_해당_사용자를_Optional로_감싸_반환한다() { + // when + final Optional actual = userRepository.findById(사용자.getId()); + + // then + assertThat(actual).contains(사용자); + } + + @Test + void 존재하지_않는_사용자_아이디를_전달하면_빈_Optional을_반환한다() { + // when + final Optional actual = userRepository.findById(존재하지_않는_사용자_아이디); + + // then + assertThat(actual).isEmpty(); + } + + @Test + void 회원탈퇴한_사용자의_id를_전달하면_빈_Optional을_반환한다() { + // when + final Optional actual = userRepository.findById(탈퇴한_사용자.getId()); + + // then + assertThat(actual).isEmpty(); + } + + @Test + void 존재하는_oauthId를_전달하면_해당_사용자를_Optional로_감싸_반환한다() { + // when + final Optional actual = userRepository.findByOauthId(사용자.getOauthInformation().getOauthId()); + + // then + assertThat(actual).contains(사용자); + } + + @Test + void 존재하지_않는_oauthId를_전달하면_해당_사용자를_빈_Optional로_반환한다() { + // when + final Optional actual = userRepository.findByOauthId(존재하지_않는_oauth_아이디); + + // then + assertThat(actual).isEmpty(); + } + + @Test + void 회원탈퇴한_사용자의_id를_전달하면_참을_반환한다() { + // when + final boolean actual = userRepository.existsByIdAndDeletedIsTrue(탈퇴한_사용자.getId()); + + // then + assertThat(actual).isTrue(); + } + + @Test + void 회원탈퇴하지_않거나_회원가입하지_않은_사용자의_id를_전달하면_거짓을_반환한다() { + // when + final boolean actual = userRepository.existsByIdAndDeletedIsTrue(사용자.getId()); + + // then + assertThat(actual).isFalse(); + } + + @Test + void 마지막_이름이_동일한_이름이_아직_없다면_거짓을_반환한다() { + // when + final boolean actual = userRepository.existsByNameEndingWith(존재하지_않는_사용자_이름); + + // then + assertThat(actual).isFalse(); + } + + @Test + void 마지막_이름이_동일한_이름이_있다면_참을_반환한다() { + // when + final boolean actual = userRepository.existsByNameEndingWith(끝이_동일한_이름); + + // then + assertThat(actual).isTrue(); + } + + @Test + void 이름이_있다면_참을_반환한다() { + // when + final boolean actual = userRepository.existsByName(존재하는_사용자_이름); + + // then + assertThat(actual).isTrue(); + } + + @Test + void 이름이_없다면_거짓을_반환한다() { + // when + final boolean actual = userRepository.existsByName(존재하지_않는_사용자_이름); + + // then + assertThat(actual).isFalse(); + } +} diff --git a/backend/ddang/src/test/java/com/ddang/ddang/user/infrastructure/persistence/fixture/JpaUserReliabilityRepositoryFixture.java b/backend/ddang/src/test/java/com/ddang/ddang/user/infrastructure/persistence/fixture/JpaUserReliabilityRepositoryFixture.java new file mode 100644 index 000000000..3a1f5af41 --- /dev/null +++ b/backend/ddang/src/test/java/com/ddang/ddang/user/infrastructure/persistence/fixture/JpaUserReliabilityRepositoryFixture.java @@ -0,0 +1,48 @@ +package com.ddang.ddang.user.infrastructure.persistence.fixture; + +import com.ddang.ddang.image.domain.ProfileImage; +import com.ddang.ddang.user.domain.Reliability; +import com.ddang.ddang.user.domain.User; +import com.ddang.ddang.user.domain.UserReliability; +import com.ddang.ddang.user.infrastructure.persistence.JpaUserReliabilityRepository; +import com.ddang.ddang.user.infrastructure.persistence.JpaUserRepository; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import org.junit.jupiter.api.BeforeEach; +import org.springframework.beans.factory.annotation.Autowired; + +@SuppressWarnings("NonAsciiCharacters") +public class JpaUserReliabilityRepositoryFixture { + + @PersistenceContext + private EntityManager em; + + @Autowired + private JpaUserRepository userRepository; + + @Autowired + private JpaUserReliabilityRepository userReliabilityRepository; + + protected User 사용자; + protected UserReliability 저장하기_전_사용자_신뢰도_엔티티; + protected UserReliability 사용자_신뢰도_정보; + + @BeforeEach + void setUp() { + 사용자 = User.builder() + .name("사용자") + .profileImage(new ProfileImage("uplad.png", "store.png")) + .reliability(Reliability.INITIAL_RELIABILITY) + .oauthId("12345") + .build(); + userRepository.save(사용자); + + 저장하기_전_사용자_신뢰도_엔티티 = new UserReliability(사용자); + + 사용자_신뢰도_정보 = new UserReliability(사용자); + userReliabilityRepository.save(사용자_신뢰도_정보); + + em.flush(); + em.clear(); + } +} diff --git a/backend/ddang/src/test/java/com/ddang/ddang/user/infrastructure/persistence/fixture/JpaUserRepositoryFixture.java b/backend/ddang/src/test/java/com/ddang/ddang/user/infrastructure/persistence/fixture/JpaUserRepositoryFixture.java index 437b21aef..c6c9be071 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/user/infrastructure/persistence/fixture/JpaUserRepositoryFixture.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/user/infrastructure/persistence/fixture/JpaUserRepositoryFixture.java @@ -23,7 +23,8 @@ public class JpaUserRepositoryFixture { protected ProfileImage 프로필_이미지 = new ProfileImage("upload.png", "store.png"); protected String 존재하지_않는_oauth_아이디 = "invalidOauthId"; protected Long 존재하지_않는_사용자_아이디 = -999L; - protected String 존재하지_않는_사용자_이름 = "새로운 이름"; + protected String 존재하지_않는_사용자_이름 = "67890"; + protected String 끝이_동일한_이름; protected String 존재하는_사용자_이름; protected User 사용자; protected User 탈퇴한_사용자; @@ -32,7 +33,7 @@ public class JpaUserRepositoryFixture { void setUp() { final ProfileImage 사용자_프로필_이미지 = new ProfileImage("upload.png", "store.png"); 사용자 = User.builder() - .name("사용자") + .name("kakao12345") .profileImage(사용자_프로필_이미지) .reliability(new Reliability(4.7d)) .oauthId("12345") @@ -48,6 +49,7 @@ void setUp() { userRepository.saveAll(List.of(사용자, 탈퇴한_사용자)); + 끝이_동일한_이름 = "12345"; 존재하는_사용자_이름 = 사용자.getName(); em.flush(); diff --git a/backend/ddang/src/test/java/com/ddang/ddang/user/infrastructure/persistence/fixture/UserReliabilityRepositoryImplFixture.java b/backend/ddang/src/test/java/com/ddang/ddang/user/infrastructure/persistence/fixture/UserReliabilityRepositoryImplFixture.java new file mode 100644 index 000000000..3cad48eb2 --- /dev/null +++ b/backend/ddang/src/test/java/com/ddang/ddang/user/infrastructure/persistence/fixture/UserReliabilityRepositoryImplFixture.java @@ -0,0 +1,47 @@ +package com.ddang.ddang.user.infrastructure.persistence.fixture; + +import com.ddang.ddang.image.domain.ProfileImage; +import com.ddang.ddang.user.domain.Reliability; +import com.ddang.ddang.user.domain.User; +import com.ddang.ddang.user.domain.UserReliability; +import com.ddang.ddang.user.domain.repository.UserReliabilityRepository; +import com.ddang.ddang.user.domain.repository.UserRepository; +import com.ddang.ddang.user.infrastructure.persistence.JpaUserReliabilityRepository; +import com.ddang.ddang.user.infrastructure.persistence.JpaUserRepository; +import com.ddang.ddang.user.infrastructure.persistence.UserReliabilityRepositoryImpl; +import com.ddang.ddang.user.infrastructure.persistence.UserRepositoryImpl; +import org.junit.jupiter.api.BeforeEach; +import org.springframework.beans.factory.annotation.Autowired; + +@SuppressWarnings("NonAsciiCharacters") +public class UserReliabilityRepositoryImplFixture { + + private UserRepository userRepository; + private UserReliabilityRepository userReliabilityRepository; + + protected User 사용자; + protected UserReliability 저장하기_전_사용자_신뢰도_엔티티; + protected UserReliability 사용자_신뢰도_정보; + + @BeforeEach + void setUp( + @Autowired final JpaUserRepository jpaUserRepository, + @Autowired final JpaUserReliabilityRepository jpaUserReliabilityRepository + ) { + userRepository = new UserRepositoryImpl(jpaUserRepository); + userReliabilityRepository = new UserReliabilityRepositoryImpl(jpaUserReliabilityRepository); + + 사용자 = User.builder() + .name("사용자") + .profileImage(new ProfileImage("uplad.png", "store.png")) + .reliability(Reliability.INITIAL_RELIABILITY) + .oauthId("12345") + .build(); + userRepository.save(사용자); + + 저장하기_전_사용자_신뢰도_엔티티 = new UserReliability(사용자); + + 사용자_신뢰도_정보 = new UserReliability(사용자); + userReliabilityRepository.save(사용자_신뢰도_정보); + } +} diff --git a/backend/ddang/src/test/java/com/ddang/ddang/user/infrastructure/persistence/fixture/UserRepositoryImplFixture.java b/backend/ddang/src/test/java/com/ddang/ddang/user/infrastructure/persistence/fixture/UserRepositoryImplFixture.java new file mode 100644 index 000000000..0d6b25d93 --- /dev/null +++ b/backend/ddang/src/test/java/com/ddang/ddang/user/infrastructure/persistence/fixture/UserRepositoryImplFixture.java @@ -0,0 +1,52 @@ +package com.ddang.ddang.user.infrastructure.persistence.fixture; + +import com.ddang.ddang.image.domain.ProfileImage; +import com.ddang.ddang.user.domain.Reliability; +import com.ddang.ddang.user.domain.User; +import com.ddang.ddang.user.domain.repository.UserRepository; +import com.ddang.ddang.user.infrastructure.persistence.JpaUserRepository; +import com.ddang.ddang.user.infrastructure.persistence.UserRepositoryImpl; +import org.junit.jupiter.api.BeforeEach; +import org.springframework.beans.factory.annotation.Autowired; + +@SuppressWarnings("NonAsciiCharacters") +public class UserRepositoryImplFixture { + + private UserRepository userRepository; + + protected ProfileImage 프로필_이미지 = new ProfileImage("upload.png", "store.png"); + protected String 존재하지_않는_oauth_아이디 = "invalidOauthId"; + protected Long 존재하지_않는_사용자_아이디 = -999L; + protected String 존재하지_않는_사용자_이름 = "67890"; + protected String 끝이_동일한_이름; + protected String 존재하는_사용자_이름; + protected User 사용자; + protected User 탈퇴한_사용자; + + @BeforeEach + void fixtureSetUp(@Autowired final JpaUserRepository jpaUserRepository) { + userRepository = new UserRepositoryImpl(jpaUserRepository); + + final ProfileImage 사용자_프로필_이미지 = new ProfileImage("upload.png", "store.png"); + 사용자 = User.builder() + .name("kakao12345") + .profileImage(사용자_프로필_이미지) + .reliability(new Reliability(4.7d)) + .oauthId("12345") + .build(); + + 탈퇴한_사용자 = User.builder() + .name("탈퇴한 사용자") + .profileImage(사용자_프로필_이미지) + .reliability(new Reliability(4.7d)) + .oauthId("12345") + .build(); + 탈퇴한_사용자.withdrawal(); + + userRepository.save(사용자); + userRepository.save(탈퇴한_사용자); + + 끝이_동일한_이름 = "12345"; + 존재하는_사용자_이름 = 사용자.getName(); + } +} diff --git a/backend/ddang/src/test/java/com/ddang/ddang/user/presentation/UserControllerTest.java b/backend/ddang/src/test/java/com/ddang/ddang/user/presentation/UserControllerTest.java index 83ca223a7..12f0cdff7 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/user/presentation/UserControllerTest.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/user/presentation/UserControllerTest.java @@ -6,6 +6,7 @@ import com.ddang.ddang.authentication.domain.TokenType; import com.ddang.ddang.authentication.domain.dto.AuthenticationStore; import com.ddang.ddang.exception.GlobalExceptionHandler; +import com.ddang.ddang.user.application.exception.AlreadyExistsNameException; import com.ddang.ddang.user.application.exception.UserNotFoundException; import com.ddang.ddang.user.presentation.fixture.UserControllerFixture; import org.junit.jupiter.api.BeforeEach; @@ -108,6 +109,22 @@ void setUp() { ); } + @Test + void 존재하지_않는_사용자_정보_조회시_404를_반환한다() throws Exception { + // given + given(tokenDecoder.decode(eq(TokenType.ACCESS), anyString())).willReturn(Optional.of(존재하지_않는_사용자_ID_클레임)); + given(userService.readById(anyLong())).willThrow(new UserNotFoundException("사용자 정보를 사용할 수 없습니다.")); + + // when & then + mockMvc.perform(get("/users") + .header(HttpHeaders.AUTHORIZATION, 액세스_토큰_값) + ) + .andExpectAll( + status().isNotFound(), + jsonPath("$.message").exists() + ); + } + @Test void 사용자_정보를_모두_수정한다() throws Exception { // given @@ -174,13 +191,35 @@ void setUp() { } @Test - void 존재하지_않는_사용자_정보_조회시_404를_반환한다() throws Exception { + void 이미_존재하는_이름으로_수정할시_400_예외를_반환한다() throws Exception { // given given(tokenDecoder.decode(eq(TokenType.ACCESS), anyString())).willReturn(Optional.of(사용자_ID_클레임)); - given(userService.readById(anyLong())).willThrow(new UserNotFoundException("사용자 정보를 사용할 수 없습니다.")); + given(userService.updateById(anyLong(), any())).willThrow(new AlreadyExistsNameException("이미 존재하는 닉네임입니다.")); // when & then - mockMvc.perform(get("/users") + mockMvc.perform(multipart(HttpMethod.PATCH, "/users") + .file(수정할_이름) + .file(프로필_이미지가_없는_경우_파일) + .contentType(MediaType.MULTIPART_FORM_DATA_VALUE) + .header(HttpHeaders.AUTHORIZATION, 액세스_토큰_값) + ) + .andExpectAll( + status().isBadRequest(), + jsonPath("$.message").exists() + ); + } + + @Test + void 존재하지_않는_사용자_정보를_수정하면_404를_반환한다() throws Exception { + // given + given(tokenDecoder.decode(eq(TokenType.ACCESS), anyString())).willReturn(Optional.of(존재하지_않는_사용자_ID_클레임)); + given(userService.updateById(anyLong(), any())).willThrow(new UserNotFoundException("사용자 정보를 사용할 수 없습니다.")); + + // when & then + mockMvc.perform(multipart(HttpMethod.PATCH, "/users") + .file(수정할_이름) + .file(프로필_이미지가_없는_경우_파일) + .contentType(MediaType.MULTIPART_FORM_DATA_VALUE) .header(HttpHeaders.AUTHORIZATION, 액세스_토큰_값) ) .andExpectAll( diff --git a/backend/ddang/src/test/java/com/ddang/ddang/user/presentation/fixture/UserAuctionControllerFixture.java b/backend/ddang/src/test/java/com/ddang/ddang/user/presentation/fixture/UserAuctionControllerFixture.java index 506a674df..eb2731dc4 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/user/presentation/fixture/UserAuctionControllerFixture.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/user/presentation/fixture/UserAuctionControllerFixture.java @@ -44,7 +44,8 @@ public class UserAuctionControllerFixture extends CommonControllerSliceTest { "판매자", 3.5d, false, - AuctionStatus.UNBIDDEN + AuctionStatus.UNBIDDEN, + null ); protected ReadAuctionDto 경매_정보_dto2 = new ReadAuctionDto( 2L, @@ -66,7 +67,8 @@ public class UserAuctionControllerFixture extends CommonControllerSliceTest { "판매자", 3.5d, false, - AuctionStatus.UNBIDDEN + AuctionStatus.UNBIDDEN, + null ); protected ReadAuctionsDto 사용자의_경매들_정보_dto = new ReadAuctionsDto(List.of(경매_정보_dto2, 경매_정보_dto1), true); protected ReadAuctionsDto 사용자가_참여한_경매들_정보_dto = new ReadAuctionsDto(List.of(경매_정보_dto2, 경매_정보_dto1), true); diff --git a/backend/ddang/src/test/java/com/ddang/ddang/user/presentation/fixture/UserControllerFixture.java b/backend/ddang/src/test/java/com/ddang/ddang/user/presentation/fixture/UserControllerFixture.java index 7d63d3ad5..fa46afbd5 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/user/presentation/fixture/UserControllerFixture.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/user/presentation/fixture/UserControllerFixture.java @@ -14,6 +14,7 @@ public class UserControllerFixture extends CommonControllerSliceTest { protected String 액세스_토큰_값 = "Bearer accessToken"; protected PrivateClaims 사용자_ID_클레임 = new PrivateClaims(1L); + protected PrivateClaims 존재하지_않는_사용자_ID_클레임 = new PrivateClaims(999L); protected String 탈퇴한_사용자_이름 = "알 수 없음"; protected ReadUserDto 사용자_정보_조회_dto = new ReadUserDto(1L, "사용자1", 1L, 4.6d, "12345", false);
Table 14. /auctions/{auctionId}/reviewsTable 13. /auctions/{auctionId}/reviews