From 7c75ba9c1b0df5259e13818be33b2b0a95ae853a Mon Sep 17 00:00:00 2001 From: minjonyyy Date: Mon, 5 May 2025 13:14:17 +0900 Subject: [PATCH 1/3] =?UTF-8?q?docs=20:=20=EC=8A=A4=EC=9B=A8=EA=B1=B0=20?= =?UTF-8?q?=EB=AC=B8=EC=84=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/parkez/queue/web/QueueController.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/parkez/queue/web/QueueController.java b/src/main/java/com/parkez/queue/web/QueueController.java index 143596b..1ecdf2b 100644 --- a/src/main/java/com/parkez/queue/web/QueueController.java +++ b/src/main/java/com/parkez/queue/web/QueueController.java @@ -6,6 +6,7 @@ import com.parkez.queue.dto.response.MyWaitingQueueDetailResponse; import com.parkez.queue.dto.response.MyWaitingQueueListResponse; import com.parkez.queue.service.QueueService; +import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; @@ -16,17 +17,19 @@ @RequestMapping("/api/waiting") @RestController @RequiredArgsConstructor -@Tag(name = "16. 대기열 API", description = "대기열 API") +@Tag(name = "18. 대기열 API", description = "대기열 API") public class QueueController { private final QueueService queueService; @GetMapping("/me") + @Operation(summary = "나의 전체 대기열 조회", description = "내가 대기 중인 모든 대기열을 조회합니다.") public Response> getMyWaitingQueues(@AuthenticatedUser @Parameter(hidden = true) AuthUser authUser) { List response = queueService.findMyWaitingQueues(authUser); return Response.of(response); } @GetMapping("/{reservationId}") + @Operation(summary = "예약에 대한 나의 대기열 조회", description = "해당 예약에 대한 나의 대기열 정보를 상세 조회합니다.") public Response getMyQueue( @AuthenticatedUser @Parameter(hidden = true) AuthUser authUser, @PathVariable Long reservationId) { @@ -35,6 +38,7 @@ public Response getMyQueue( } @DeleteMapping("/{reservationId}") + @Operation(summary = "예약에 대한 나의 대기열 취소", description = "해당 예약에 대한 나의 대기열을 취소합니다.") public Response cancelMyQueue( @AuthenticatedUser @Parameter(hidden = true) AuthUser authUser, @PathVariable Long reservationId) { From 85a4e245e229b7a133baf4637e99f0aab3b7f936 Mon Sep 17 00:00:00 2001 From: codingTrip Date: Mon, 5 May 2025 14:29:10 +0900 Subject: [PATCH 2/3] =?UTF-8?q?fix:=20=EC=98=88=EC=95=BD=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=20=EB=8F=99=EC=8B=9C=EC=84=B1=20=EC=98=A4=EB=A5=98=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20-=20@Transactional=EC=9D=84=20=EB=9D=BD=20?= =?UTF-8?q?=EC=95=88=EC=97=90=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/ReservationProcessor.java | 134 +++ .../service/ReservationService.java | 72 +- .../service/ReservationProcessorTest.java | 802 ++++++++++++++++++ .../service/ReservationServiceTest.java | 629 +------------- 4 files changed, 942 insertions(+), 695 deletions(-) create mode 100644 src/main/java/com/parkez/reservation/service/ReservationProcessor.java create mode 100644 src/test/java/com/parkez/reservation/service/ReservationProcessorTest.java diff --git a/src/main/java/com/parkez/reservation/service/ReservationProcessor.java b/src/main/java/com/parkez/reservation/service/ReservationProcessor.java new file mode 100644 index 0000000..9a6bf57 --- /dev/null +++ b/src/main/java/com/parkez/reservation/service/ReservationProcessor.java @@ -0,0 +1,134 @@ +package com.parkez.reservation.service; + +import com.parkez.common.exception.ParkingEasyException; +import com.parkez.common.principal.AuthUser; +import com.parkez.parkingzone.domain.entity.ParkingZone; +import com.parkez.parkingzone.domain.enums.ParkingZoneStatus; +import com.parkez.parkingzone.service.ParkingZoneReader; +import com.parkez.promotion.domain.entity.Coupon; +import com.parkez.promotion.domain.entity.PromotionIssue; +import com.parkez.promotion.excption.PromotionIssueErrorCode; +import com.parkez.promotion.service.PromotionIssueReader; +import com.parkez.promotion.service.PromotionIssueValidator; +import com.parkez.promotion.service.PromotionIssueWriter; +import com.parkez.queue.domain.enums.JoinQueueResult; +import com.parkez.queue.exception.QueueErrorCode; +import com.parkez.queue.service.QueueService; +import com.parkez.reservation.domain.entity.Reservation; +import com.parkez.reservation.domain.enums.ReservationStatus; +import com.parkez.reservation.dto.request.ReservationRequest; +import com.parkez.reservation.dto.response.ReservationResponse; +import com.parkez.reservation.exception.ReservationErrorCode; +import com.parkez.user.domain.entity.User; +import com.parkez.user.service.UserReader; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.time.temporal.ChronoUnit; +import java.util.List; + +@Component +@RequiredArgsConstructor +public class ReservationProcessor { + + private final ReservationReader reservationReader; + private final ReservationWriter reservationWriter; + private final UserReader userReader; + private final ParkingZoneReader parkingZoneReader; + private final PromotionIssueReader promotionIssueReader; + private final PromotionIssueValidator promotionIssueValidator; + private final PromotionIssueWriter promotionIssueWriter; + private final QueueService queueService; + + @Transactional + public ReservationResponse create(AuthUser authUser, ReservationRequest request, LocalDateTime now) { + User user = userReader.getActiveUserById(authUser.getId()); + ParkingZone parkingZone = parkingZoneReader.getActiveByParkingZoneId(request.getParkingZoneId()); + + if (!validateRequestTime(request)) { + throw new ParkingEasyException(ReservationErrorCode.NOT_VALID_REQUEST_TIME); + } + + if (!parkingZone.getStatus().equals(ParkingZoneStatus.AVAILABLE)) { + throw new ParkingEasyException(ReservationErrorCode.CANT_RESERVE_UNAVAILABLE_PARKING_ZONE); + } + + if (!parkingZone.isOpened(request.getStartDateTime(), request.getEndDateTime())) { + throw new ParkingEasyException(ReservationErrorCode.CANT_RESERVE_AT_CLOSE_TIME); + } + + List statusList = List.of(ReservationStatus.PENDING, ReservationStatus.CONFIRMED); + + if (reservationReader.existsReservationByConditionsForUser(parkingZone, request.getStartDateTime(), request.getEndDateTime(), user.getId(), statusList)) { + throw new ParkingEasyException(ReservationErrorCode.ALREADY_RESERVED_BY_YOURSELF); + } + + boolean existed = reservationReader.existsReservationByConditions(parkingZone, request.getStartDateTime(), request.getEndDateTime(), statusList); + + if (existed) { + handleJoinQueue(user, request); + return null; + } + + long hours = calculateUsedHour(request.getStartDateTime(), request.getEndDateTime()); + + BigDecimal originalPrice = parkingZone.getParkingLotPricePerHour().multiply(BigDecimal.valueOf(hours)); + BigDecimal discountAmount = BigDecimal.ZERO; + + Long promotionIssueId = null; + if (request.getPromotionIssueId() != null) { + PromotionIssue promotionIssue = promotionIssueReader.getWithPromotionAndCouponById(request.getPromotionIssueId()); + + if (!promotionIssue.isOwnedBy(authUser.getId())) { + throw new ParkingEasyException(PromotionIssueErrorCode.NOT_YOUR_COUPON); + } + + promotionIssueId = promotionIssue.getId(); + promotionIssueValidator.validateCanBeUsed(promotionIssue, now); + Coupon coupon = promotionIssue.getCoupon(); + discountAmount = coupon.calculateDiscount(originalPrice); + + promotionIssueWriter.use(promotionIssue, now); + } + + BigDecimal finalPrice = originalPrice.subtract(discountAmount); + + Reservation reservation = reservationWriter.create( + user, + parkingZone, + request.getStartDateTime(), + request.getEndDateTime(), + originalPrice, + discountAmount, + finalPrice, + promotionIssueId + ); + + return ReservationResponse.from(reservation); + } + + private boolean validateRequestTime(ReservationRequest request) { + LocalDateTime startDateTime = request.getStartDateTime(); + LocalDateTime endDateTime = request.getEndDateTime(); + + return startDateTime.isBefore(endDateTime) + && startDateTime.isAfter(LocalDateTime.now()) + && startDateTime.toLocalDate().equals(endDateTime.toLocalDate()); + } + + private long calculateUsedHour(LocalDateTime startDateTime, LocalDateTime endDateTime) { + return ChronoUnit.HOURS.between(startDateTime, endDateTime); + } + + private void handleJoinQueue(User user, ReservationRequest request) { + JoinQueueResult result = queueService.joinWaitingQueue(user.getId(), request); + + if (result == JoinQueueResult.ALREADY_JOINED) { + throw new ParkingEasyException(QueueErrorCode.ALREADY_IN_QUEUE); + } + } + +} diff --git a/src/main/java/com/parkez/reservation/service/ReservationService.java b/src/main/java/com/parkez/reservation/service/ReservationService.java index f556059..f5f9605 100644 --- a/src/main/java/com/parkez/reservation/service/ReservationService.java +++ b/src/main/java/com/parkez/reservation/service/ReservationService.java @@ -36,6 +36,7 @@ import org.springframework.data.domain.Sort; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.support.TransactionSynchronizationManager; import java.math.BigDecimal; import java.time.LocalDateTime; @@ -58,80 +59,14 @@ public class ReservationService { private final PromotionIssueValidator promotionIssueValidator; private final PromotionIssueWriter promotionIssueWriter; private final QueueService queueService; + private final ReservationProcessor reservationProcessor; private static final long CANCEL_LIMIT_HOURS = 1L; private static final long EXPIRATION_TIME = 10L; - @Transactional public ReservationResponse createReservation(AuthUser authUser, ReservationRequest request, LocalDateTime now) { try { - return distributedLockManager.executeWithLock(request.getParkingZoneId(), () -> { - - User user = userReader.getActiveUserById(authUser.getId()); - ParkingZone parkingZone = parkingZoneReader.getActiveByParkingZoneId(request.getParkingZoneId()); - - if (!validateRequestTime(request)) { - throw new ParkingEasyException(ReservationErrorCode.NOT_VALID_REQUEST_TIME); - } - - if (!parkingZone.getStatus().equals(ParkingZoneStatus.AVAILABLE)) { - throw new ParkingEasyException(ReservationErrorCode.CANT_RESERVE_UNAVAILABLE_PARKING_ZONE); - } - - if (!parkingZone.isOpened(request.getStartDateTime(), request.getEndDateTime())) { - throw new ParkingEasyException(ReservationErrorCode.CANT_RESERVE_AT_CLOSE_TIME); - } - - List statusList = List.of(ReservationStatus.PENDING, ReservationStatus.CONFIRMED); - - if (reservationReader.existsReservationByConditionsForUser(parkingZone, request.getStartDateTime(), request.getEndDateTime(), user.getId(), statusList)) { - throw new ParkingEasyException(ReservationErrorCode.ALREADY_RESERVED_BY_YOURSELF); - } - - boolean existed = reservationReader.existsReservationByConditions(parkingZone, request.getStartDateTime(), request.getEndDateTime(), statusList); - - if (existed) { - handleJoinQueue(user, request); - return null; - } - - long hours = calculateUsedHour(request.getStartDateTime(), request.getEndDateTime()); - - BigDecimal originalPrice = parkingZone.getParkingLotPricePerHour().multiply(BigDecimal.valueOf(hours)); - BigDecimal discountAmount = BigDecimal.ZERO; - - Long promotionIssueId = null; - if (request.getPromotionIssueId() != null) { - PromotionIssue promotionIssue = promotionIssueReader.getWithPromotionAndCouponById(request.getPromotionIssueId()); - - if (!promotionIssue.isOwnedBy(authUser.getId())) { - throw new ParkingEasyException(PromotionIssueErrorCode.NOT_YOUR_COUPON); - } - - promotionIssueId = promotionIssue.getId(); - promotionIssueValidator.validateCanBeUsed(promotionIssue, now); - Coupon coupon = promotionIssue.getCoupon(); - discountAmount = coupon.calculateDiscount(originalPrice); - - promotionIssueWriter.use(promotionIssue, now); - } - - BigDecimal finalPrice = originalPrice.subtract(discountAmount); - - Reservation reservation = reservationWriter.create( - user, - parkingZone, - request.getStartDateTime(), - request.getEndDateTime(), - originalPrice, - discountAmount, - finalPrice, - promotionIssueId - ); - - return ReservationResponse.from(reservation); - }); - + return distributedLockManager.executeWithLock(request.getParkingZoneId(), () -> reservationProcessor.create(authUser, request, now)); } catch (ParkingEasyException e) { if (e.getErrorCode() == ReservationErrorCode.RESERVATION_LOCK_FAILED) { // 락 선점 실패 → 대기열 등록은 완료했으므로 예약 생성 결과 데이터는 null 반환 @@ -142,7 +77,6 @@ public ReservationResponse createReservation(AuthUser authUser, ReservationReque } } - public Page getMyReservations(AuthUser authUser, int page, int size) { int adjustedPage = page - 1; diff --git a/src/test/java/com/parkez/reservation/service/ReservationProcessorTest.java b/src/test/java/com/parkez/reservation/service/ReservationProcessorTest.java new file mode 100644 index 0000000..bd59962 --- /dev/null +++ b/src/test/java/com/parkez/reservation/service/ReservationProcessorTest.java @@ -0,0 +1,802 @@ +package com.parkez.reservation.service; + +import com.parkez.common.exception.ParkingEasyException; +import com.parkez.common.principal.AuthUser; +import com.parkez.parkinglot.domain.entity.ParkingLot; +import com.parkez.parkingzone.domain.entity.ParkingZone; +import com.parkez.parkingzone.domain.enums.ParkingZoneStatus; +import com.parkez.parkingzone.service.ParkingZoneReader; +import com.parkez.promotion.domain.entity.Coupon; +import com.parkez.promotion.domain.entity.Promotion; +import com.parkez.promotion.domain.entity.PromotionIssue; +import com.parkez.promotion.domain.enums.DiscountType; +import com.parkez.promotion.domain.enums.PromotionStatus; +import com.parkez.promotion.domain.enums.PromotionType; +import com.parkez.promotion.service.PromotionIssueReader; +import com.parkez.promotion.service.PromotionIssueValidator; +import com.parkez.promotion.service.PromotionIssueWriter; +import com.parkez.queue.domain.enums.JoinQueueResult; +import com.parkez.queue.service.QueueService; +import com.parkez.reservation.distributedlockmanager.DistributedLockManager; +import com.parkez.reservation.domain.entity.Reservation; +import com.parkez.reservation.domain.enums.ReservationStatus; +import com.parkez.reservation.dto.request.ReservationRequest; +import com.parkez.reservation.dto.response.ReservationResponse; +import com.parkez.reservation.exception.ReservationErrorCode; +import com.parkez.user.domain.entity.User; +import com.parkez.user.domain.enums.UserRole; +import com.parkez.user.service.UserReader; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.test.util.ReflectionTestUtils; + +import java.math.BigDecimal; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.temporal.ChronoUnit; +import java.util.List; + +import static com.parkez.promotion.excption.PromotionIssueErrorCode.*; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.doThrow; + +@ExtendWith(MockitoExtension.class) +class ReservationProcessorTest { + + @Mock + private ReservationReader reservationReader; + @Mock + private ReservationWriter reservationWriter; + @Mock + private UserReader userReader; + @Mock + private ParkingZoneReader parkingZoneReader; + + @Mock + private DistributedLockManager distributedLockManager; + + @Mock + private PromotionIssueWriter promotionIssueWriter; + + @Mock + private PromotionIssueReader promotionIssueReader; + + @Mock + private PromotionIssueValidator promotionIssueValidator; + + @Mock + private QueueService queueService; + + @InjectMocks + private ReservationProcessor reservationProcessor; + + private static final LocalTime OPENED_AT = LocalTime.of(9, 0, 0); + private static final LocalTime CLOSED_AT = LocalTime.of(21, 0, 0); + private static final LocalDateTime RESERVATION_START_DATE_TIME = LocalDateTime.of(LocalDate.now().plusDays(1), OPENED_AT); + private static final LocalDateTime RESERVATION_END_DATE_TIME = LocalDateTime.of(LocalDate.now().plusDays(1), CLOSED_AT); + + private static AuthUser createAuthUser(Long id) { + return AuthUser.builder() + .id(id) + .email("test@example.com") + .roleName(UserRole.Authority.USER) + .nickname("test") + .build(); + } + + private static AuthUser createAuthOwner(Long id) { + return AuthUser.builder() + .id(id) + .email("test@example.com") + .roleName(UserRole.Authority.OWNER) + .nickname("test") + .build(); + } + + private static User createUser(Long id) { + User user = User.builder().build(); + ReflectionTestUtils.setField(user, "id", id); + return user; + } + + private static User createOwner(Long id) { + User owner = User.builder().build(); + ReflectionTestUtils.setField(owner, "id", id); + return owner; + } + + private static ParkingLot createParkingLot(Long id, User owner) { + ParkingLot parkingLot = ParkingLot.builder() + .owner(owner) + .pricePerHour(BigDecimal.valueOf(2000)) + .name("test") + .openedAt(OPENED_AT) + .closedAt(CLOSED_AT) + .build(); + ReflectionTestUtils.setField(parkingLot, "id", id); + return parkingLot; + } + + private static ParkingZone createParkingZone(Long id, ParkingLot parkingLot) { + ParkingZone parkingZone = ParkingZone.builder() + .parkingLot(parkingLot) + .build(); + ReflectionTestUtils.setField(parkingZone, "id", id); + ReflectionTestUtils.setField(parkingZone, "status", ParkingZoneStatus.AVAILABLE); + return parkingZone; + } + + private static Reservation create(Long id, User user, ParkingZone parkingZone, ReservationRequest request, BigDecimal price, + BigDecimal originalPrice, BigDecimal discountAmount, Long promotionIssueId) { + Reservation reservation = Reservation.builder() + .user(user) + .parkingZone(parkingZone) + .parkingLotName(parkingZone.getParkingLotName()) + .startDateTime(request.getStartDateTime()) + .endDateTime(request.getEndDateTime()) + .price(price) + .originalPrice(originalPrice) + .discountAmount(discountAmount) + .promotionIssueId(promotionIssueId) + .build(); + ReflectionTestUtils.setField(reservation, "id", id); + return reservation; + } + + private static Reservation getReservation(Long id, User user, ParkingZone parkingZone) { + Reservation reservation = Reservation.builder() + .user(user) + .parkingZone(parkingZone) + .parkingLotName(parkingZone.getParkingLotName()) + .build(); + ReflectionTestUtils.setField(reservation, "id", id); + return reservation; + } + + private static ReservationRequest createRequest(Long id, Long promotionIssueId) { + ReservationRequest request = new ReservationRequest(); + ReflectionTestUtils.setField(request, "parkingZoneId", id); + ReflectionTestUtils.setField(request, "startDateTime", RESERVATION_START_DATE_TIME); + ReflectionTestUtils.setField(request, "endDateTime", RESERVATION_END_DATE_TIME); + ReflectionTestUtils.setField(request, "promotionIssueId", promotionIssueId); + return request; + } + + private PromotionIssue createPromotionIssue(Promotion promotion, AuthUser authUser, LocalDateTime issuedAt, + LocalDateTime expiresAt) { + return PromotionIssue.builder() + .promotion(promotion) + .user(User.from(authUser)) + .issuedAt(issuedAt) + .expiresAt(expiresAt) + .build(); + } + + private Promotion createPromotion(Long promotionId, String promotionName, PromotionType promotionType, + Coupon coupon, int limitTotal, + int limitPerUser, LocalDateTime promotionStartAt, LocalDateTime promotionEndAt, int validDaysAfterIssue, + PromotionStatus promotionStatus) { + Promotion promotion = Promotion.builder() + .name(promotionName) + .promotionType(promotionType) + .coupon(coupon) + .limitTotal(limitTotal) + .limitPerUser(limitPerUser) + .promotionStartAt(promotionStartAt) + .promotionEndAt(promotionEndAt) + .validDaysAfterIssue(validDaysAfterIssue) + .build(); + ReflectionTestUtils.setField(promotion, "id", promotionId); + ReflectionTestUtils.setField(promotion, "promotionStatus", promotionStatus); + return promotion; + } + + private Coupon createCoupon(Long id, String name, DiscountType discountType, int discountValue, + String description) { + Coupon coupon = Coupon.builder() + .name(name) + .discountType(discountType) + .discountValue(discountValue) + .description(description) + .build(); + ReflectionTestUtils.setField(coupon, "id", id); + return coupon; + } + + private static ReservationRequest createRequest() { + LocalDateTime start = LocalDateTime.now().plusHours(1); + LocalDateTime end = start.plusHours(1); + + return new ReservationRequest(1L, start, end); + } + + @Nested + class create { + + @Test + void 특정_주차공간에_존재하지않는_쿠폰으로_예약하면_PROMOTION_ISSUE_NOT_FOUND_예외_발생한다() { + // given + Long ownerId = 1L; + Long userId = 2L; + Long parkingLotId = 1L; + Long parkingZoneId = 1L; + + AuthUser authUser = createAuthUser(userId); + + Long promotionIssueId = -1L; + ReservationRequest request = createRequest(parkingZoneId, promotionIssueId); + + User owner = createOwner(ownerId); + User user = createUser(authUser.getId()); + + ParkingLot parkingLot = createParkingLot(parkingLotId, owner); + + ParkingZone parkingZone = createParkingZone(parkingZoneId, parkingLot); + + given(userReader.getActiveUserById(anyLong())).willReturn(user); + given(parkingZoneReader.getActiveByParkingZoneId(anyLong())).willReturn(parkingZone); + given(promotionIssueReader.getWithPromotionAndCouponById(anyLong())).willThrow(new ParkingEasyException(PROMOTION_ISSUE_NOT_FOUND)); + + // when & then + assertThatThrownBy(()->reservationProcessor.create(authUser, request, LocalDateTime.now())) + .isInstanceOf(ParkingEasyException.class) + .hasMessage(PROMOTION_ISSUE_NOT_FOUND.getDefaultMessage()); + + } + + @Test + void 만료된_쿠폰으로_예약하면_EXPIRED_COUPON_예외가_발생한다() { + // given + Long ownerId = 1L; + Long userId = 2L; + Long parkingLotId = 1L; + Long parkingZoneId = 1L; + + AuthUser authUser = createAuthUser(userId); + + Long promotionIssueId = 1L; + ReservationRequest request = createRequest(parkingZoneId, promotionIssueId); + + User owner = createOwner(ownerId); + User user = createUser(authUser.getId()); + + ParkingLot parkingLot = createParkingLot(parkingLotId, owner); + + ParkingZone parkingZone = createParkingZone(parkingZoneId, parkingLot); + + Long promotionId = 1L; + String promotionName = "DAILY 2000"; + PromotionType promotionType = PromotionType.DAILY; + int limitTotal = 100; + int limitPerUser = 1; + int validDaysAfterIssue = 3; + + LocalDateTime promotionStartAt = LocalDateTime.now().plusDays(1); + LocalDateTime promotionEndAt = LocalDateTime.now().plusDays(2); + + long couponId = 1L; + String couponName = "신규가입 2000원 할인 쿠폰"; + DiscountType discountType = DiscountType.FIXED; + int discountValue = 2000; + String description = "신규 유저 전용, 1회만 사용 가능"; + + Coupon coupon = createCoupon(couponId,couponName,discountType,discountValue,description); + Promotion promotion = createPromotion(promotionId,promotionName,promotionType,coupon,limitTotal,limitPerUser,promotionStartAt,promotionEndAt,validDaysAfterIssue, PromotionStatus.ACTIVE); + + LocalDateTime issuedAt = LocalDateTime.now(); + LocalDateTime expiresAt = issuedAt.plusDays(promotion.getValidDaysAfterIssue()); + PromotionIssue promotionIssue = createPromotionIssue(promotion, authUser, issuedAt, expiresAt); + + + + given(userReader.getActiveUserById(anyLong())).willReturn(user); + given(parkingZoneReader.getActiveByParkingZoneId(anyLong())).willReturn(parkingZone); + given(promotionIssueReader.getWithPromotionAndCouponById(anyLong())).willReturn(promotionIssue); + doThrow(new ParkingEasyException(EXPIRED_COUPON)).when(promotionIssueValidator).validateCanBeUsed(any(),any()); + + // when & then + assertThatThrownBy(()->reservationProcessor.create(authUser, request, LocalDateTime.now())) + .isInstanceOf(ParkingEasyException.class) + .hasMessage(EXPIRED_COUPON.getDefaultMessage()); + + } + + @Test + void 이미_사용한_쿠폰으로_예약하면_ALREADY_USED_예외가_발생한다() { + // given + Long ownerId = 1L; + Long userId = 2L; + Long parkingLotId = 1L; + Long parkingZoneId = 1L; + + AuthUser authUser = createAuthUser(userId); + + Long promotionIssueId = 1L; + ReservationRequest request = createRequest(parkingZoneId, promotionIssueId); + + User owner = createOwner(ownerId); + User user = createUser(authUser.getId()); + + ParkingLot parkingLot = createParkingLot(parkingLotId, owner); + + ParkingZone parkingZone = createParkingZone(parkingZoneId, parkingLot); + + Long promotionId = 1L; + String promotionName = "DAILY 2000"; + PromotionType promotionType = PromotionType.DAILY; + int limitTotal = 100; + int limitPerUser = 1; + int validDaysAfterIssue = 3; + + LocalDateTime promotionStartAt = LocalDateTime.now().plusDays(1); + LocalDateTime promotionEndAt = LocalDateTime.now().plusDays(2); + + long couponId = 1L; + String couponName = "신규가입 2000원 할인 쿠폰"; + DiscountType discountType = DiscountType.FIXED; + int discountValue = 2000; + String description = "신규 유저 전용, 1회만 사용 가능"; + + Coupon coupon = createCoupon(couponId,couponName,discountType,discountValue,description); + Promotion promotion = createPromotion(promotionId,promotionName,promotionType,coupon,limitTotal,limitPerUser,promotionStartAt,promotionEndAt,validDaysAfterIssue,PromotionStatus.ACTIVE); + + LocalDateTime issuedAt = LocalDateTime.now(); + LocalDateTime expiresAt = issuedAt.plusDays(promotion.getValidDaysAfterIssue()); + PromotionIssue promotionIssue = createPromotionIssue(promotion, authUser, issuedAt, expiresAt); + + + + given(userReader.getActiveUserById(anyLong())).willReturn(user); + given(parkingZoneReader.getActiveByParkingZoneId(anyLong())).willReturn(parkingZone); + given(promotionIssueReader.getWithPromotionAndCouponById(anyLong())).willReturn(promotionIssue); + doThrow(new ParkingEasyException(ALREADY_USED)).when(promotionIssueValidator).validateCanBeUsed(any(),any()); + + // when & then + assertThatThrownBy(()->reservationProcessor.create(authUser, request, LocalDateTime.now())) + .isInstanceOf(ParkingEasyException.class) + .hasMessage(ALREADY_USED.getDefaultMessage()); + + } + + @Test + void 다른_사람의_쿠폰으로_예약하면_NOT_YOUR_COUPON_예외가_발생한다() { + // given + Long ownerId = 1L; + Long userId = 2L; + Long parkingLotId = 1L; + Long parkingZoneId = 1L; + + AuthUser authUser = createAuthUser(userId); + AuthUser anotherAuthUser = createAuthUser(3L); + + Long promotionIssueId = 1L; + ReservationRequest request = createRequest(parkingZoneId, promotionIssueId); + + User owner = createOwner(ownerId); + User user = createUser(authUser.getId()); + + ParkingLot parkingLot = createParkingLot(parkingLotId, owner); + + ParkingZone parkingZone = createParkingZone(parkingZoneId, parkingLot); + + Long promotionId = 1L; + String promotionName = "DAILY 2000"; + PromotionType promotionType = PromotionType.DAILY; + int limitTotal = 100; + int limitPerUser = 1; + int validDaysAfterIssue = 3; + + LocalDateTime promotionStartAt = LocalDateTime.now().plusDays(1); + LocalDateTime promotionEndAt = LocalDateTime.now().plusDays(2); + + long couponId = 1L; + String couponName = "신규가입 2000원 할인 쿠폰"; + DiscountType discountType = DiscountType.FIXED; + int discountValue = 2000; + String description = "신규 유저 전용, 1회만 사용 가능"; + + Coupon coupon = createCoupon(couponId,couponName,discountType,discountValue,description); + Promotion promotion = createPromotion(promotionId,promotionName,promotionType,coupon,limitTotal,limitPerUser,promotionStartAt,promotionEndAt,validDaysAfterIssue,PromotionStatus.ACTIVE); + + LocalDateTime issuedAt = LocalDateTime.now(); + LocalDateTime expiresAt = issuedAt.plusDays(promotion.getValidDaysAfterIssue()); + PromotionIssue promotionIssue = createPromotionIssue(promotion, anotherAuthUser, issuedAt, expiresAt); + + + + given(userReader.getActiveUserById(anyLong())).willReturn(user); + given(parkingZoneReader.getActiveByParkingZoneId(anyLong())).willReturn(parkingZone); + given(promotionIssueReader.getWithPromotionAndCouponById(anyLong())).willReturn(promotionIssue); + + // when & then + assertThatThrownBy(()->reservationProcessor.create(authUser, request, LocalDateTime.now())) + .isInstanceOf(ParkingEasyException.class) + .hasMessage(NOT_YOUR_COUPON.getDefaultMessage()); + + } + + @Test + void 특정_주차공간에_쿠폰을_적용해서_예약_생성_할_수_있다() { + // given + Long ownerId = 1L; + Long userId = 2L; + Long parkingLotId = 1L; + Long parkingZoneId = 1L; + Long reservationId = 1L; + + AuthUser authUser = createAuthUser(userId); + + Long promotionIssueId = 1L; + ReservationRequest request = createRequest(parkingZoneId, promotionIssueId); + + User owner = createOwner(ownerId); + User user = createUser(authUser.getId()); + + ParkingLot parkingLot = createParkingLot(parkingLotId, owner); + + ParkingZone parkingZone = createParkingZone(parkingZoneId, parkingLot); + + Long promotionId = 1L; + String promotionName = "DAILY 2000"; + PromotionType promotionType = PromotionType.DAILY; + int limitTotal = 100; + int limitPerUser = 1; + int validDaysAfterIssue = 3; + + LocalDateTime promotionStartAt = LocalDateTime.now().plusDays(1); + LocalDateTime promotionEndAt = LocalDateTime.now().plusDays(2); + + long couponId = 1L; + String couponName = "신규가입 2000원 할인 쿠폰"; + DiscountType discountType = DiscountType.FIXED; + int discountValue = 2000; + String description = "신규 유저 전용, 1회만 사용 가능"; + + + + Coupon coupon = createCoupon(couponId,couponName,discountType,discountValue,description); + Promotion promotion = createPromotion(promotionId,promotionName,promotionType,coupon,limitTotal,limitPerUser,promotionStartAt,promotionEndAt,validDaysAfterIssue,PromotionStatus.ACTIVE); + + LocalDateTime issuedAt = LocalDateTime.now(); + LocalDateTime expiresAt = issuedAt.plusDays(promotion.getValidDaysAfterIssue()); + PromotionIssue promotionIssue = createPromotionIssue(promotion, authUser, issuedAt, expiresAt); + + long hours = ChronoUnit.HOURS.between(request.getStartDateTime(), request.getEndDateTime()); + BigDecimal originalPrice = parkingZone.getParkingLotPricePerHour().multiply(BigDecimal.valueOf(hours)); + BigDecimal discountAmount = coupon.calculateDiscount(originalPrice); + BigDecimal finalPrice = originalPrice.subtract(discountAmount); + + Reservation reservation = create(reservationId, user, parkingZone, request, finalPrice, originalPrice, + discountAmount, promotionIssueId); + + + + given(userReader.getActiveUserById(anyLong())).willReturn(user); + given(parkingZoneReader.getActiveByParkingZoneId(anyLong())).willReturn(parkingZone); + given(promotionIssueReader.getWithPromotionAndCouponById(anyLong())).willReturn(promotionIssue); + given(reservationWriter.create(any(User.class), any(ParkingZone.class), any(LocalDateTime.class), any(LocalDateTime.class),any(BigDecimal.class),any( + BigDecimal.class),any(BigDecimal.class),any())) + .willReturn(reservation); + + // when + ReservationResponse result = reservationProcessor.create(authUser, request, LocalDateTime.now()); + + // then + assertThat(result) + .isNotNull() + .extracting("reservationId", "userId", "parkingZoneId", "parkingLotName", "reviewWritten", "startDateTime", "endDateTime", "price", "originalPrice", "discountAmount") + .isEqualTo( + List.of(reservationId, userId, parkingZoneId, parkingLot.getName(), false, request.getStartDateTime(), request.getEndDateTime(), finalPrice,originalPrice, discountAmount) + ); + } + + @Test + void 특정_주차공간에_쿠폰을_사용하지않고_예약_생성_할_수_있다() { + // given + Long ownerId = 1L; + Long userId = 2L; + Long parkingLotId = 1L; + Long parkingZoneId = 1L; + Long reservationId = 1L; + + AuthUser authUser = createAuthUser(userId); + + ReservationRequest request = createRequest(parkingZoneId, null); + + User owner = createOwner(ownerId); + User user = createUser(authUser.getId()); + + ParkingLot parkingLot = createParkingLot(parkingLotId, owner); + + ParkingZone parkingZone = createParkingZone(parkingZoneId, parkingLot); + + long hours = ChronoUnit.HOURS.between(request.getStartDateTime(), request.getEndDateTime()); + BigDecimal originalPrice = parkingZone.getParkingLotPricePerHour().multiply(BigDecimal.valueOf(hours)); + BigDecimal discountAmount = BigDecimal.ZERO; + BigDecimal price = originalPrice.subtract(discountAmount); + Long promotionIssueId = null; + + Reservation reservation = create(reservationId, user, parkingZone, request, price, originalPrice, + discountAmount, promotionIssueId); + + + given(userReader.getActiveUserById(anyLong())).willReturn(user); + given(parkingZoneReader.getActiveByParkingZoneId(anyLong())).willReturn(parkingZone); + given(reservationWriter.create(any(User.class), any(ParkingZone.class), any(LocalDateTime.class), any(LocalDateTime.class),any(BigDecimal.class),any( + BigDecimal.class),any(BigDecimal.class),any())) + .willReturn(reservation); + + // when + ReservationResponse result = reservationProcessor.create(authUser, request, LocalDateTime.now()); + + // then + assertThat(result) + .isNotNull() + .extracting("reservationId", "userId", "parkingZoneId", "parkingLotName", "reviewWritten", "startDateTime", "endDateTime", "price") + .isEqualTo( + List.of(reservationId, userId, parkingZoneId, parkingLot.getName(), false, request.getStartDateTime(), request.getEndDateTime(), price) + ); + } + + @Test + void 특정_주차공간에_대한_예약_생성_시_시작_시간이_종료_시간보다_늦을_경우_NOT_VALID_REQUEST_TIME_예외_처리() { + // given + Long ownerId = 1L; + Long userId = 2L; + Long parkingLotId = 1L; + Long parkingZoneId = 1L; + + AuthUser authUser = createAuthUser(userId); + + ReservationRequest request = createRequest(parkingZoneId, null); + ReflectionTestUtils.setField(request, "endDateTime", request.getStartDateTime().minusNanos(1)); + + User owner = createOwner(ownerId); + User user = createUser(authUser.getId()); + + ParkingLot parkingLot = createParkingLot(parkingLotId, owner); + + ParkingZone parkingZone = createParkingZone(parkingZoneId, parkingLot); + + + given(userReader.getActiveUserById(anyLong())).willReturn(user); + given(parkingZoneReader.getActiveByParkingZoneId(anyLong())).willReturn(parkingZone); + + // when & then + ParkingEasyException exception = assertThrows(ParkingEasyException.class, + () -> reservationProcessor.create(authUser, request, LocalDateTime.now())); + assertThat(exception.getErrorCode()).isEqualTo(ReservationErrorCode.NOT_VALID_REQUEST_TIME); + } + + @Test + void 특정_주차공간에_대한_예약_생성_시_과거_시간에_예약을_하는_경우_NOT_VALID_REQUEST_TIME_예외_처리() { + // given + Long ownerId = 1L; + Long userId = 2L; + Long parkingLotId = 1L; + Long parkingZoneId = 1L; + + LocalDateTime startDateTime = LocalDateTime.now().minusNanos(1); + + AuthUser authUser = createAuthUser(userId); + + ReservationRequest request = createRequest(parkingZoneId, null); + ReflectionTestUtils.setField(request, "startDateTime", startDateTime); + + User owner = createOwner(ownerId); + User user = createUser(authUser.getId()); + + ParkingLot parkingLot = createParkingLot(parkingLotId, owner); + + ParkingZone parkingZone = createParkingZone(parkingZoneId, parkingLot); + + + given(userReader.getActiveUserById(anyLong())).willReturn(user); + given(parkingZoneReader.getActiveByParkingZoneId(anyLong())).willReturn(parkingZone); + + // when & then + ParkingEasyException exception = assertThrows(ParkingEasyException.class, + () -> reservationProcessor.create(authUser, request, LocalDateTime.now())); + assertThat(exception.getErrorCode()).isEqualTo(ReservationErrorCode.NOT_VALID_REQUEST_TIME); + } + + @Test + void 특정_주차공간에_대한_예약_생성_시_예약_기간이_하루를_초과하여_예약을_하는_경우_NOT_VALID_REQUEST_TIME_예외_처리() { + // given + Long ownerId = 1L; + Long userId = 2L; + Long parkingLotId = 1L; + Long parkingZoneId = 1L; + + LocalDateTime endDateTime = RESERVATION_END_DATE_TIME.plusDays(1); + + AuthUser authUser = createAuthUser(userId); + + ReservationRequest request = createRequest(parkingZoneId, null); + ReflectionTestUtils.setField(request, "endDateTime", endDateTime); + + User owner = createOwner(ownerId); + User user = createUser(authUser.getId()); + + ParkingLot parkingLot = createParkingLot(parkingLotId, owner); + + ParkingZone parkingZone = createParkingZone(parkingZoneId, parkingLot); + + given(userReader.getActiveUserById(anyLong())).willReturn(user); + given(parkingZoneReader.getActiveByParkingZoneId(anyLong())).willReturn(parkingZone); + + // when & then + ParkingEasyException exception = assertThrows(ParkingEasyException.class, + () -> reservationProcessor.create(authUser, request, LocalDateTime.now())); + assertThat(exception.getErrorCode()).isEqualTo(ReservationErrorCode.NOT_VALID_REQUEST_TIME); + } + + @Test + void 특정_주차공간에_대한_예약_생성_시_parkingZone_의_상태가_UNAVAILABLE_일_경우_CANT_RESERVE_UNAVAILABLE_PARKING_ZONE_예외_처리() { + // given + Long ownerId = 1L; + Long userId = 2L; + Long parkingLotId = 1L; + Long parkingZoneId = 1L; + + AuthUser authUser = createAuthUser(userId); + + ReservationRequest request = createRequest(parkingZoneId, null); + + User owner = createOwner(ownerId); + User user = createUser(authUser.getId()); + + ParkingLot parkingLot = createParkingLot(parkingLotId, owner); + + ParkingZone parkingZone = createParkingZone(parkingZoneId, parkingLot); + ReflectionTestUtils.setField(parkingZone, "status", ParkingZoneStatus.UNAVAILABLE); + + + given(userReader.getActiveUserById(anyLong())).willReturn(user); + given(parkingZoneReader.getActiveByParkingZoneId(anyLong())).willReturn(parkingZone); + + // when & then + ParkingEasyException exception = assertThrows(ParkingEasyException.class, + () -> reservationProcessor.create(authUser, request, LocalDateTime.now())); + assertThat(exception.getErrorCode()).isEqualTo(ReservationErrorCode.CANT_RESERVE_UNAVAILABLE_PARKING_ZONE); + } + + @Test + void 특정_주차공간에_대한_예약_생성_시_예약_시작_시간이_OPEND_AT_이전일_경우_CANT_RESERVE_AT_CLOSE_TIME_예외_처리() { + // given + Long ownerId = 1L; + Long userId = 2L; + Long parkingLotId = 1L; + Long parkingZoneId = 1L; + + LocalDateTime startDateTime = RESERVATION_START_DATE_TIME.minusNanos(1); + + AuthUser authUser = createAuthUser(userId); + + ReservationRequest request = createRequest(parkingZoneId, null); + ReflectionTestUtils.setField(request, "startDateTime", startDateTime); + + User owner = createOwner(ownerId); + User user = createUser(authUser.getId()); + + ParkingLot parkingLot = createParkingLot(parkingLotId, owner); + + ParkingZone parkingZone = createParkingZone(parkingZoneId, parkingLot); + + + given(userReader.getActiveUserById(anyLong())).willReturn(user); + given(parkingZoneReader.getActiveByParkingZoneId(anyLong())).willReturn(parkingZone); + + // when & then + ParkingEasyException exception = assertThrows(ParkingEasyException.class, + () -> reservationProcessor.create(authUser, request, LocalDateTime.now())); + assertThat(exception.getErrorCode()).isEqualTo(ReservationErrorCode.CANT_RESERVE_AT_CLOSE_TIME); + } + + @Test + void 특정_주차공간에_대한_예약_생성_시_예약_종료_시간이_CLOSED_AT_이후일_경우_CANT_RESERVE_AT_CLOSE_TIME_예외_처리() { + // given + Long ownerId = 1L; + Long userId = 2L; + Long parkingLotId = 1L; + Long parkingZoneId = 1L; + + LocalDateTime endDateTime = RESERVATION_END_DATE_TIME.plusNanos(1); + + AuthUser authUser = createAuthUser(userId); + + ReservationRequest request = createRequest(parkingZoneId, null); + ReflectionTestUtils.setField(request, "endDateTime", endDateTime); + + User owner = createOwner(ownerId); + User user = createUser(authUser.getId()); + + ParkingLot parkingLot = createParkingLot(parkingLotId, owner); + + ParkingZone parkingZone = createParkingZone(parkingZoneId, parkingLot); + + + given(userReader.getActiveUserById(anyLong())).willReturn(user); + given(parkingZoneReader.getActiveByParkingZoneId(anyLong())).willReturn(parkingZone); + + // when & then + ParkingEasyException exception = assertThrows(ParkingEasyException.class, + () -> reservationProcessor.create(authUser, request, LocalDateTime.now())); + assertThat(exception.getErrorCode()).isEqualTo(ReservationErrorCode.CANT_RESERVE_AT_CLOSE_TIME); + } + + @Test + void 특정_주차공간에_대한_예약_생성_시_이미_해당_시간에_예약이_존재하면_대기열_등록되고_null_반환() { + // given + Long ownerId = 1L; + Long userId = 2L; + Long parkingLotId = 1L; + Long parkingZoneId = 1L; + + AuthUser authUser = createAuthUser(userId); + + ReservationRequest request = createRequest(parkingZoneId, null); + + User owner = createOwner(ownerId); + User user = createUser(authUser.getId()); + + ParkingLot parkingLot = createParkingLot(parkingLotId, owner); + ParkingZone parkingZone = createParkingZone(parkingZoneId, parkingLot); + + + given(userReader.getActiveUserById(anyLong())).willReturn(user); + given(parkingZoneReader.getActiveByParkingZoneId(anyLong())).willReturn(parkingZone); + given(reservationReader.existsReservationByConditions(any(ParkingZone.class), any(LocalDateTime.class), any(LocalDateTime.class), anyList())).willReturn(true); + given(queueService.joinWaitingQueue(anyLong(), any())).willReturn(JoinQueueResult.JOINED); + + // when + ReservationResponse response = reservationProcessor.create(authUser, request, LocalDateTime.now()); + + // then + assertThat(response).isNull(); + } + + @Test + void 본인이_이미_예약한_경우_예외발생() { + // given + Long ownerId = 1L; + Long userId = 2L; + Long parkingLotId = 1L; + Long parkingZoneId = 1L; + + AuthUser authUser = createAuthUser(userId); + + ReservationRequest request = createRequest(parkingZoneId, null); + + User owner = createOwner(ownerId); + User user = createUser(authUser.getId()); + + ParkingLot parkingLot = createParkingLot(parkingLotId, owner); + ParkingZone parkingZone = createParkingZone(parkingZoneId, parkingLot); + + List statusList = List.of(ReservationStatus.PENDING, ReservationStatus.CONFIRMED); + + + given(userReader.getActiveUserById(userId)).willReturn(user); + given(parkingZoneReader.getActiveByParkingZoneId(parkingZoneId)).willReturn(parkingZone); + given(reservationReader.existsReservationByConditionsForUser(parkingZone, request.getStartDateTime(), request.getEndDateTime(), userId, statusList)) + .willReturn(true); + + // when & then + ParkingEasyException exception = assertThrows(ParkingEasyException.class, + () -> reservationProcessor.create(authUser, request, LocalDateTime.now())); + + assertThat(exception.getErrorCode()).isEqualTo(ReservationErrorCode.ALREADY_RESERVED_BY_YOURSELF); + } + } + +} \ No newline at end of file diff --git a/src/test/java/com/parkez/reservation/service/ReservationServiceTest.java b/src/test/java/com/parkez/reservation/service/ReservationServiceTest.java index c915392..f33ac6b 100644 --- a/src/test/java/com/parkez/reservation/service/ReservationServiceTest.java +++ b/src/test/java/com/parkez/reservation/service/ReservationServiceTest.java @@ -237,589 +237,17 @@ private static ReservationRequest createReservationRequest() { @Nested class CreateReservation { - - @Test - void 특정_주차공간에_존재하지않는_쿠폰으로_예약하면_PROMOTION_ISSUE_NOT_FOUND_예외_발생한다() { - // given - Long ownerId = 1L; - Long userId = 2L; - Long parkingLotId = 1L; - Long parkingZoneId = 1L; - - AuthUser authUser = createAuthUser(userId); - - Long promotionIssueId = -1L; - ReservationRequest request = createRequest(parkingZoneId, promotionIssueId); - - User owner = createOwner(ownerId); - User user = createUser(authUser.getId()); - - ParkingLot parkingLot = createParkingLot(parkingLotId, owner); - - ParkingZone parkingZone = createParkingZone(parkingZoneId, parkingLot); - - given(distributedLockManager.executeWithLock(anyLong(), any())).willAnswer(invocation -> { - Callable task = invocation.getArgument(1); - return task.call(); - }); - given(userReader.getActiveUserById(anyLong())).willReturn(user); - given(parkingZoneReader.getActiveByParkingZoneId(anyLong())).willReturn(parkingZone); - given(promotionIssueReader.getWithPromotionAndCouponById(anyLong())).willThrow(new ParkingEasyException(PROMOTION_ISSUE_NOT_FOUND)); - - // when & then - assertThatThrownBy(()->reservationService.createReservation(authUser, request, LocalDateTime.now())) - .isInstanceOf(ParkingEasyException.class) - .hasMessage(PROMOTION_ISSUE_NOT_FOUND.getDefaultMessage()); - - } - - @Test - void 만료된_쿠폰으로_예약하면_EXPIRED_COUPON_예외가_발생한다() { - // given - Long ownerId = 1L; - Long userId = 2L; - Long parkingLotId = 1L; - Long parkingZoneId = 1L; - - AuthUser authUser = createAuthUser(userId); - - Long promotionIssueId = 1L; - ReservationRequest request = createRequest(parkingZoneId, promotionIssueId); - - User owner = createOwner(ownerId); - User user = createUser(authUser.getId()); - - ParkingLot parkingLot = createParkingLot(parkingLotId, owner); - - ParkingZone parkingZone = createParkingZone(parkingZoneId, parkingLot); - - Long promotionId = 1L; - String promotionName = "DAILY 2000"; - PromotionType promotionType = PromotionType.DAILY; - int limitTotal = 100; - int limitPerUser = 1; - int validDaysAfterIssue = 3; - - LocalDateTime promotionStartAt = LocalDateTime.now().plusDays(1); - LocalDateTime promotionEndAt = LocalDateTime.now().plusDays(2); - - long couponId = 1L; - String couponName = "신규가입 2000원 할인 쿠폰"; - DiscountType discountType = DiscountType.FIXED; - int discountValue = 2000; - String description = "신규 유저 전용, 1회만 사용 가능"; - - Coupon coupon = createCoupon(couponId,couponName,discountType,discountValue,description); - Promotion promotion = createPromotion(promotionId,promotionName,promotionType,coupon,limitTotal,limitPerUser,promotionStartAt,promotionEndAt,validDaysAfterIssue,PromotionStatus.ACTIVE); - - LocalDateTime issuedAt = LocalDateTime.now(); - LocalDateTime expiresAt = issuedAt.plusDays(promotion.getValidDaysAfterIssue()); - PromotionIssue promotionIssue = createPromotionIssue(promotion, authUser, issuedAt, expiresAt); - - - given(distributedLockManager.executeWithLock(anyLong(), any())).willAnswer(invocation -> { - Callable task = invocation.getArgument(1); - return task.call(); - }); - given(userReader.getActiveUserById(anyLong())).willReturn(user); - given(parkingZoneReader.getActiveByParkingZoneId(anyLong())).willReturn(parkingZone); - given(promotionIssueReader.getWithPromotionAndCouponById(anyLong())).willReturn(promotionIssue); - doThrow(new ParkingEasyException(EXPIRED_COUPON)).when(promotionIssueValidator).validateCanBeUsed(any(),any()); - - // when & then - assertThatThrownBy(()->reservationService.createReservation(authUser, request, LocalDateTime.now())) - .isInstanceOf(ParkingEasyException.class) - .hasMessage(EXPIRED_COUPON.getDefaultMessage()); - - } - - @Test - void 이미_사용한_쿠폰으로_예약하면_ALREADY_USED_예외가_발생한다() { - // given - Long ownerId = 1L; - Long userId = 2L; - Long parkingLotId = 1L; - Long parkingZoneId = 1L; - - AuthUser authUser = createAuthUser(userId); - - Long promotionIssueId = 1L; - ReservationRequest request = createRequest(parkingZoneId, promotionIssueId); - - User owner = createOwner(ownerId); - User user = createUser(authUser.getId()); - - ParkingLot parkingLot = createParkingLot(parkingLotId, owner); - - ParkingZone parkingZone = createParkingZone(parkingZoneId, parkingLot); - - Long promotionId = 1L; - String promotionName = "DAILY 2000"; - PromotionType promotionType = PromotionType.DAILY; - int limitTotal = 100; - int limitPerUser = 1; - int validDaysAfterIssue = 3; - - LocalDateTime promotionStartAt = LocalDateTime.now().plusDays(1); - LocalDateTime promotionEndAt = LocalDateTime.now().plusDays(2); - - long couponId = 1L; - String couponName = "신규가입 2000원 할인 쿠폰"; - DiscountType discountType = DiscountType.FIXED; - int discountValue = 2000; - String description = "신규 유저 전용, 1회만 사용 가능"; - - Coupon coupon = createCoupon(couponId,couponName,discountType,discountValue,description); - Promotion promotion = createPromotion(promotionId,promotionName,promotionType,coupon,limitTotal,limitPerUser,promotionStartAt,promotionEndAt,validDaysAfterIssue,PromotionStatus.ACTIVE); - - LocalDateTime issuedAt = LocalDateTime.now(); - LocalDateTime expiresAt = issuedAt.plusDays(promotion.getValidDaysAfterIssue()); - PromotionIssue promotionIssue = createPromotionIssue(promotion, authUser, issuedAt, expiresAt); - - - given(distributedLockManager.executeWithLock(anyLong(), any())).willAnswer(invocation -> { - Callable task = invocation.getArgument(1); - return task.call(); - }); - given(userReader.getActiveUserById(anyLong())).willReturn(user); - given(parkingZoneReader.getActiveByParkingZoneId(anyLong())).willReturn(parkingZone); - given(promotionIssueReader.getWithPromotionAndCouponById(anyLong())).willReturn(promotionIssue); - doThrow(new ParkingEasyException(ALREADY_USED)).when(promotionIssueValidator).validateCanBeUsed(any(),any()); - - // when & then - assertThatThrownBy(()->reservationService.createReservation(authUser, request, LocalDateTime.now())) - .isInstanceOf(ParkingEasyException.class) - .hasMessage(ALREADY_USED.getDefaultMessage()); - - } - - @Test - void 다른_사람의_쿠폰으로_예약하면_NOT_YOUR_COUPON_예외가_발생한다() { - // given - Long ownerId = 1L; - Long userId = 2L; - Long parkingLotId = 1L; - Long parkingZoneId = 1L; - - AuthUser authUser = createAuthUser(userId); - AuthUser anotherAuthUser = createAuthUser(3L); - - Long promotionIssueId = 1L; - ReservationRequest request = createRequest(parkingZoneId, promotionIssueId); - - User owner = createOwner(ownerId); - User user = createUser(authUser.getId()); - - ParkingLot parkingLot = createParkingLot(parkingLotId, owner); - - ParkingZone parkingZone = createParkingZone(parkingZoneId, parkingLot); - - Long promotionId = 1L; - String promotionName = "DAILY 2000"; - PromotionType promotionType = PromotionType.DAILY; - int limitTotal = 100; - int limitPerUser = 1; - int validDaysAfterIssue = 3; - - LocalDateTime promotionStartAt = LocalDateTime.now().plusDays(1); - LocalDateTime promotionEndAt = LocalDateTime.now().plusDays(2); - - long couponId = 1L; - String couponName = "신규가입 2000원 할인 쿠폰"; - DiscountType discountType = DiscountType.FIXED; - int discountValue = 2000; - String description = "신규 유저 전용, 1회만 사용 가능"; - - Coupon coupon = createCoupon(couponId,couponName,discountType,discountValue,description); - Promotion promotion = createPromotion(promotionId,promotionName,promotionType,coupon,limitTotal,limitPerUser,promotionStartAt,promotionEndAt,validDaysAfterIssue,PromotionStatus.ACTIVE); - - LocalDateTime issuedAt = LocalDateTime.now(); - LocalDateTime expiresAt = issuedAt.plusDays(promotion.getValidDaysAfterIssue()); - PromotionIssue promotionIssue = createPromotionIssue(promotion, anotherAuthUser, issuedAt, expiresAt); - - - given(distributedLockManager.executeWithLock(anyLong(), any())).willAnswer(invocation -> { - Callable task = invocation.getArgument(1); - return task.call(); - }); - given(userReader.getActiveUserById(anyLong())).willReturn(user); - given(parkingZoneReader.getActiveByParkingZoneId(anyLong())).willReturn(parkingZone); - given(promotionIssueReader.getWithPromotionAndCouponById(anyLong())).willReturn(promotionIssue); - - // when & then - assertThatThrownBy(()->reservationService.createReservation(authUser, request, LocalDateTime.now())) - .isInstanceOf(ParkingEasyException.class) - .hasMessage(NOT_YOUR_COUPON.getDefaultMessage()); - - } - - @Test - void 특정_주차공간에_쿠폰을_적용해서_예약_생성_할_수_있다() { - // given - Long ownerId = 1L; - Long userId = 2L; - Long parkingLotId = 1L; - Long parkingZoneId = 1L; - Long reservationId = 1L; - - AuthUser authUser = createAuthUser(userId); - - Long promotionIssueId = 1L; - ReservationRequest request = createRequest(parkingZoneId, promotionIssueId); - - User owner = createOwner(ownerId); - User user = createUser(authUser.getId()); - - ParkingLot parkingLot = createParkingLot(parkingLotId, owner); - - ParkingZone parkingZone = createParkingZone(parkingZoneId, parkingLot); - - Long promotionId = 1L; - String promotionName = "DAILY 2000"; - PromotionType promotionType = PromotionType.DAILY; - int limitTotal = 100; - int limitPerUser = 1; - int validDaysAfterIssue = 3; - - LocalDateTime promotionStartAt = LocalDateTime.now().plusDays(1); - LocalDateTime promotionEndAt = LocalDateTime.now().plusDays(2); - - long couponId = 1L; - String couponName = "신규가입 2000원 할인 쿠폰"; - DiscountType discountType = DiscountType.FIXED; - int discountValue = 2000; - String description = "신규 유저 전용, 1회만 사용 가능"; - - - - Coupon coupon = createCoupon(couponId,couponName,discountType,discountValue,description); - Promotion promotion = createPromotion(promotionId,promotionName,promotionType,coupon,limitTotal,limitPerUser,promotionStartAt,promotionEndAt,validDaysAfterIssue,PromotionStatus.ACTIVE); - - LocalDateTime issuedAt = LocalDateTime.now(); - LocalDateTime expiresAt = issuedAt.plusDays(promotion.getValidDaysAfterIssue()); - PromotionIssue promotionIssue = createPromotionIssue(promotion, authUser, issuedAt, expiresAt); - - long hours = ChronoUnit.HOURS.between(request.getStartDateTime(), request.getEndDateTime()); - BigDecimal originalPrice = parkingZone.getParkingLotPricePerHour().multiply(BigDecimal.valueOf(hours)); - BigDecimal discountAmount = coupon.calculateDiscount(originalPrice); - BigDecimal finalPrice = originalPrice.subtract(discountAmount); - - Reservation reservation = createReservation(reservationId, user, parkingZone, request, finalPrice, originalPrice, - discountAmount, promotionIssueId); - - - given(distributedLockManager.executeWithLock(anyLong(), any())).willAnswer(invocation -> { - Callable task = invocation.getArgument(1); - return task.call(); - }); - given(userReader.getActiveUserById(anyLong())).willReturn(user); - given(parkingZoneReader.getActiveByParkingZoneId(anyLong())).willReturn(parkingZone); - given(promotionIssueReader.getWithPromotionAndCouponById(anyLong())).willReturn(promotionIssue); - given(reservationWriter.create(any(User.class), any(ParkingZone.class), any(LocalDateTime.class), any(LocalDateTime.class),any(BigDecimal.class),any( - BigDecimal.class),any(BigDecimal.class),any())) - .willReturn(reservation); - - // when - ReservationResponse result = reservationService.createReservation(authUser, request, LocalDateTime.now()); - - // then - assertThat(result) - .isNotNull() - .extracting("reservationId", "userId", "parkingZoneId", "parkingLotName", "reviewWritten", "startDateTime", "endDateTime", "price", "originalPrice", "discountAmount") - .isEqualTo( - List.of(reservationId, userId, parkingZoneId, parkingLot.getName(), false, request.getStartDateTime(), request.getEndDateTime(), finalPrice,originalPrice, discountAmount) - ); - } - - @Test - void 특정_주차공간에_쿠폰을_사용하지않고_예약_생성_할_수_있다() { - // given - Long ownerId = 1L; - Long userId = 2L; - Long parkingLotId = 1L; - Long parkingZoneId = 1L; - Long reservationId = 1L; - - AuthUser authUser = createAuthUser(userId); - - ReservationRequest request = createRequest(parkingZoneId, null); - - User owner = createOwner(ownerId); - User user = createUser(authUser.getId()); - - ParkingLot parkingLot = createParkingLot(parkingLotId, owner); - - ParkingZone parkingZone = createParkingZone(parkingZoneId, parkingLot); - - long hours = ChronoUnit.HOURS.between(request.getStartDateTime(), request.getEndDateTime()); - BigDecimal originalPrice = parkingZone.getParkingLotPricePerHour().multiply(BigDecimal.valueOf(hours)); - BigDecimal discountAmount = BigDecimal.ZERO; - BigDecimal price = originalPrice.subtract(discountAmount); - Long promotionIssueId = null; - - Reservation reservation = createReservation(reservationId, user, parkingZone, request, price, originalPrice, - discountAmount, promotionIssueId); - - given(distributedLockManager.executeWithLock(anyLong(), any())).willAnswer(invocation -> { - Callable task = invocation.getArgument(1); - return task.call(); - }); - given(userReader.getActiveUserById(anyLong())).willReturn(user); - given(parkingZoneReader.getActiveByParkingZoneId(anyLong())).willReturn(parkingZone); - given(reservationWriter.create(any(User.class), any(ParkingZone.class), any(LocalDateTime.class), any(LocalDateTime.class),any(BigDecimal.class),any( - BigDecimal.class),any(BigDecimal.class),any())) - .willReturn(reservation); - - // when - ReservationResponse result = reservationService.createReservation(authUser, request, LocalDateTime.now()); - - // then - assertThat(result) - .isNotNull() - .extracting("reservationId", "userId", "parkingZoneId", "parkingLotName", "reviewWritten", "startDateTime", "endDateTime", "price") - .isEqualTo( - List.of(reservationId, userId, parkingZoneId, parkingLot.getName(), false, request.getStartDateTime(), request.getEndDateTime(), price) - ); - } - - @Test - void 특정_주차공간에_대한_예약_생성_시_시작_시간이_종료_시간보다_늦을_경우_NOT_VALID_REQUEST_TIME_예외_처리() { - // given - Long ownerId = 1L; - Long userId = 2L; - Long parkingLotId = 1L; - Long parkingZoneId = 1L; - - AuthUser authUser = createAuthUser(userId); - - ReservationRequest request = createRequest(parkingZoneId, null); - ReflectionTestUtils.setField(request, "endDateTime", request.getStartDateTime().minusNanos(1)); - - User owner = createOwner(ownerId); - User user = createUser(authUser.getId()); - - ParkingLot parkingLot = createParkingLot(parkingLotId, owner); - - ParkingZone parkingZone = createParkingZone(parkingZoneId, parkingLot); - - given(distributedLockManager.executeWithLock(anyLong(), any())).willAnswer(invocation -> { - Callable task = invocation.getArgument(1); - return task.call(); - }); - given(userReader.getActiveUserById(anyLong())).willReturn(user); - given(parkingZoneReader.getActiveByParkingZoneId(anyLong())).willReturn(parkingZone); - - // when & then - ParkingEasyException exception = assertThrows(ParkingEasyException.class, - () -> reservationService.createReservation(authUser, request, LocalDateTime.now())); - assertThat(exception.getErrorCode()).isEqualTo(ReservationErrorCode.NOT_VALID_REQUEST_TIME); - } - @Test - void 특정_주차공간에_대한_예약_생성_시_과거_시간에_예약을_하는_경우_NOT_VALID_REQUEST_TIME_예외_처리() { - // given - Long ownerId = 1L; - Long userId = 2L; - Long parkingLotId = 1L; - Long parkingZoneId = 1L; - - LocalDateTime startDateTime = LocalDateTime.now().minusNanos(1); - - AuthUser authUser = createAuthUser(userId); - - ReservationRequest request = createRequest(parkingZoneId, null); - ReflectionTestUtils.setField(request, "startDateTime", startDateTime); - - User owner = createOwner(ownerId); - User user = createUser(authUser.getId()); - - ParkingLot parkingLot = createParkingLot(parkingLotId, owner); - - ParkingZone parkingZone = createParkingZone(parkingZoneId, parkingLot); - - given(distributedLockManager.executeWithLock(anyLong(), any())).willAnswer(invocation -> { - Callable task = invocation.getArgument(1); - return task.call(); - }); - given(userReader.getActiveUserById(anyLong())).willReturn(user); - given(parkingZoneReader.getActiveByParkingZoneId(anyLong())).willReturn(parkingZone); - - // when & then - ParkingEasyException exception = assertThrows(ParkingEasyException.class, - () -> reservationService.createReservation(authUser, request, LocalDateTime.now())); - assertThat(exception.getErrorCode()).isEqualTo(ReservationErrorCode.NOT_VALID_REQUEST_TIME); - } - - @Test - void 특정_주차공간에_대한_예약_생성_시_예약_기간이_하루를_초과하여_예약을_하는_경우_NOT_VALID_REQUEST_TIME_예외_처리() { - // given - Long ownerId = 1L; - Long userId = 2L; - Long parkingLotId = 1L; - Long parkingZoneId = 1L; - - LocalDateTime endDateTime = RESERVATION_END_DATE_TIME.plusDays(1); - - AuthUser authUser = createAuthUser(userId); - - ReservationRequest request = createRequest(parkingZoneId, null); - ReflectionTestUtils.setField(request, "endDateTime", endDateTime); - - User owner = createOwner(ownerId); - User user = createUser(authUser.getId()); - - ParkingLot parkingLot = createParkingLot(parkingLotId, owner); - - ParkingZone parkingZone = createParkingZone(parkingZoneId, parkingLot); - - given(distributedLockManager.executeWithLock(anyLong(), any())).willAnswer(invocation -> { - Callable task = invocation.getArgument(1); - return task.call(); - }); - given(userReader.getActiveUserById(anyLong())).willReturn(user); - given(parkingZoneReader.getActiveByParkingZoneId(anyLong())).willReturn(parkingZone); - - // when & then - ParkingEasyException exception = assertThrows(ParkingEasyException.class, - () -> reservationService.createReservation(authUser, request, LocalDateTime.now())); - assertThat(exception.getErrorCode()).isEqualTo(ReservationErrorCode.NOT_VALID_REQUEST_TIME); - } - - @Test - void 특정_주차공간에_대한_예약_생성_시_parkingZone_의_상태가_UNAVAILABLE_일_경우_CANT_RESERVE_UNAVAILABLE_PARKING_ZONE_예외_처리() { - // given - Long ownerId = 1L; - Long userId = 2L; - Long parkingLotId = 1L; - Long parkingZoneId = 1L; - - AuthUser authUser = createAuthUser(userId); - - ReservationRequest request = createRequest(parkingZoneId, null); - - User owner = createOwner(ownerId); - User user = createUser(authUser.getId()); - - ParkingLot parkingLot = createParkingLot(parkingLotId, owner); - - ParkingZone parkingZone = createParkingZone(parkingZoneId, parkingLot); - ReflectionTestUtils.setField(parkingZone, "status", ParkingZoneStatus.UNAVAILABLE); - - given(distributedLockManager.executeWithLock(anyLong(), any())).willAnswer(invocation -> { - Callable task = invocation.getArgument(1); - return task.call(); - }); - given(userReader.getActiveUserById(anyLong())).willReturn(user); - given(parkingZoneReader.getActiveByParkingZoneId(anyLong())).willReturn(parkingZone); - - // when & then - ParkingEasyException exception = assertThrows(ParkingEasyException.class, - () -> reservationService.createReservation(authUser, request, LocalDateTime.now())); - assertThat(exception.getErrorCode()).isEqualTo(ReservationErrorCode.CANT_RESERVE_UNAVAILABLE_PARKING_ZONE); - } - - @Test - void 특정_주차공간에_대한_예약_생성_시_예약_시작_시간이_OPEND_AT_이전일_경우_CANT_RESERVE_AT_CLOSE_TIME_예외_처리() { - // given - Long ownerId = 1L; - Long userId = 2L; - Long parkingLotId = 1L; - Long parkingZoneId = 1L; - - LocalDateTime startDateTime = RESERVATION_START_DATE_TIME.minusNanos(1); - - AuthUser authUser = createAuthUser(userId); - - ReservationRequest request = createRequest(parkingZoneId, null); - ReflectionTestUtils.setField(request, "startDateTime", startDateTime); - - User owner = createOwner(ownerId); - User user = createUser(authUser.getId()); - - ParkingLot parkingLot = createParkingLot(parkingLotId, owner); - - ParkingZone parkingZone = createParkingZone(parkingZoneId, parkingLot); - - given(distributedLockManager.executeWithLock(anyLong(), any())).willAnswer(invocation -> { - Callable task = invocation.getArgument(1); - return task.call(); - }); - given(userReader.getActiveUserById(anyLong())).willReturn(user); - given(parkingZoneReader.getActiveByParkingZoneId(anyLong())).willReturn(parkingZone); - - // when & then - ParkingEasyException exception = assertThrows(ParkingEasyException.class, - () -> reservationService.createReservation(authUser, request, LocalDateTime.now())); - assertThat(exception.getErrorCode()).isEqualTo(ReservationErrorCode.CANT_RESERVE_AT_CLOSE_TIME); - } - - @Test - void 특정_주차공간에_대한_예약_생성_시_예약_종료_시간이_CLOSED_AT_이후일_경우_CANT_RESERVE_AT_CLOSE_TIME_예외_처리() { - // given - Long ownerId = 1L; - Long userId = 2L; - Long parkingLotId = 1L; - Long parkingZoneId = 1L; - - LocalDateTime endDateTime = RESERVATION_END_DATE_TIME.plusNanos(1); - - AuthUser authUser = createAuthUser(userId); - - ReservationRequest request = createRequest(parkingZoneId, null); - ReflectionTestUtils.setField(request, "endDateTime", endDateTime); - - User owner = createOwner(ownerId); - User user = createUser(authUser.getId()); - - ParkingLot parkingLot = createParkingLot(parkingLotId, owner); - - ParkingZone parkingZone = createParkingZone(parkingZoneId, parkingLot); - - given(distributedLockManager.executeWithLock(anyLong(), any())).willAnswer(invocation -> { - Callable task = invocation.getArgument(1); - return task.call(); - }); - given(userReader.getActiveUserById(anyLong())).willReturn(user); - given(parkingZoneReader.getActiveByParkingZoneId(anyLong())).willReturn(parkingZone); - - // when & then - ParkingEasyException exception = assertThrows(ParkingEasyException.class, - () -> reservationService.createReservation(authUser, request, LocalDateTime.now())); - assertThat(exception.getErrorCode()).isEqualTo(ReservationErrorCode.CANT_RESERVE_AT_CLOSE_TIME); - } - - @Test - void 특정_주차공간에_대한_예약_생성_시_이미_해당_시간에_예약이_존재하면_대기열_등록되고_null_반환() { + void 올바른_예약_요청_시간에_대한_검증_테스트() { // given - Long ownerId = 1L; - Long userId = 2L; - Long parkingLotId = 1L; Long parkingZoneId = 1L; - - AuthUser authUser = createAuthUser(userId); - ReservationRequest request = createRequest(parkingZoneId, null); - User owner = createOwner(ownerId); - User user = createUser(authUser.getId()); - - ParkingLot parkingLot = createParkingLot(parkingLotId, owner); - ParkingZone parkingZone = createParkingZone(parkingZoneId, parkingLot); - - given(distributedLockManager.executeWithLock(anyLong(), any())).willAnswer(invocation -> { - Callable task = invocation.getArgument(1); - return task.call(); - }); - given(userReader.getActiveUserById(anyLong())).willReturn(user); - given(parkingZoneReader.getActiveByParkingZoneId(anyLong())).willReturn(parkingZone); - given(reservationReader.existsReservationByConditions(any(ParkingZone.class), any(LocalDateTime.class), any(LocalDateTime.class), anyList())).willReturn(true); - given(queueService.joinWaitingQueue(anyLong(), any())).willReturn(JoinQueueResult.JOINED); - // when - ReservationResponse response = reservationService.createReservation(authUser, request, LocalDateTime.now()); + boolean result = reservationService.validateRequestTime(request); // then - assertThat(response).isNull(); + assertThat(result).isTrue(); } @Test @@ -842,57 +270,6 @@ class CreateReservation { // then assertThat(result).isNull(); } - - - - @Test - void 올바른_예약_요청_시간에_대한_검증_테스트() { - // given - Long parkingZoneId = 1L; - ReservationRequest request = createRequest(parkingZoneId, null); - - // when - boolean result = reservationService.validateRequestTime(request); - - // then - assertThat(result).isTrue(); - } - - @Test - void 본인이_이미_예약한_경우_예외발생() { - // given - Long ownerId = 1L; - Long userId = 2L; - Long parkingLotId = 1L; - Long parkingZoneId = 1L; - - AuthUser authUser = createAuthUser(userId); - - ReservationRequest request = createRequest(parkingZoneId, null); - - User owner = createOwner(ownerId); - User user = createUser(authUser.getId()); - - ParkingLot parkingLot = createParkingLot(parkingLotId, owner); - ParkingZone parkingZone = createParkingZone(parkingZoneId, parkingLot); - - List statusList = List.of(ReservationStatus.PENDING, ReservationStatus.CONFIRMED); - - given(distributedLockManager.executeWithLock(anyLong(), any())).willAnswer(invocation -> { - Callable task = invocation.getArgument(1); - return task.call(); - }); - given(userReader.getActiveUserById(userId)).willReturn(user); - given(parkingZoneReader.getActiveByParkingZoneId(parkingZoneId)).willReturn(parkingZone); - given(reservationReader.existsReservationByConditionsForUser(parkingZone, request.getStartDateTime(), request.getEndDateTime(), userId, statusList)) - .willReturn(true); - - // when & then - ParkingEasyException exception = assertThrows(ParkingEasyException.class, - () -> reservationService.createReservation(authUser, request, LocalDateTime.now())); - - assertThat(exception.getErrorCode()).isEqualTo(ReservationErrorCode.ALREADY_RESERVED_BY_YOURSELF); - } } @Nested From 88dba24cf6565b1d4c0e7bcb3dd4d45d744c0faf Mon Sep 17 00:00:00 2001 From: codingTrip Date: Mon, 5 May 2025 14:42:09 +0900 Subject: [PATCH 3/3] =?UTF-8?q?fix:=20@Transactional=20=EC=A0=81=EC=9A=A9?= =?UTF-8?q?=20-=20=ED=95=98=EB=82=98=EC=9D=98=20=EC=98=81=EC=86=8D?= =?UTF-8?q?=EC=84=B1=20=EC=BB=A8=ED=85=8D=EC=8A=A4=ED=8A=B8=EB=A1=9C=20?= =?UTF-8?q?=EB=AC=B6=EC=9D=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/parkez/parkinglot/service/ParkingLotService.java | 1 + .../java/com/parkez/reservation/service/ReservationService.java | 1 + .../java/com/parkez/settlement/service/SettlementService.java | 1 + 3 files changed, 3 insertions(+) diff --git a/src/main/java/com/parkez/parkinglot/service/ParkingLotService.java b/src/main/java/com/parkez/parkinglot/service/ParkingLotService.java index d580728..d524ad7 100644 --- a/src/main/java/com/parkez/parkinglot/service/ParkingLotService.java +++ b/src/main/java/com/parkez/parkinglot/service/ParkingLotService.java @@ -196,6 +196,7 @@ public void updateParkingLotImages(AuthUser authUser, Long parkingLotId, Parking } // 주차장 삭제 + @Transactional public void deleteParkingLot(AuthUser authUser, Long parkingLotId) { Long userId = authUser.getId(); ParkingLot parkingLot = parkingLotReader.getOwnedParkingLot(userId, parkingLotId); diff --git a/src/main/java/com/parkez/reservation/service/ReservationService.java b/src/main/java/com/parkez/reservation/service/ReservationService.java index f5f9605..65b91ad 100644 --- a/src/main/java/com/parkez/reservation/service/ReservationService.java +++ b/src/main/java/com/parkez/reservation/service/ReservationService.java @@ -114,6 +114,7 @@ public Page getOwnerReservations(AuthUser authUser, Long pa return pageReservations.map(ReservationResponse::from); } + @Transactional public void completeReservation(AuthUser authUser, Long reservationId) { Reservation reservation = reservationReader.findMyReservation(authUser.getId(), reservationId); diff --git a/src/main/java/com/parkez/settlement/service/SettlementService.java b/src/main/java/com/parkez/settlement/service/SettlementService.java index d7b3e7d..605ca5a 100644 --- a/src/main/java/com/parkez/settlement/service/SettlementService.java +++ b/src/main/java/com/parkez/settlement/service/SettlementService.java @@ -129,6 +129,7 @@ private SettlementAmounts calculateSettlementAmounts(List payments) { return new SettlementAmounts(totalAmount, totalFee, netAmount); } + @Transactional public void completeSettlement(Long settlementId) { Settlement settlement = settlementReader.getById(settlementId);