diff --git a/src/test/java/roomescape/reservation/business/ReservationServiceTest.java b/src/test/java/roomescape/reservation/business/ReservationServiceTest.java deleted file mode 100644 index d9a2a2c9..00000000 --- a/src/test/java/roomescape/reservation/business/ReservationServiceTest.java +++ /dev/null @@ -1,239 +0,0 @@ -package roomescape.reservation.business; - -import static org.assertj.core.api.Assertions.*; - -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.time.LocalTime; - -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.context.annotation.Import; -import org.springframework.test.context.jdbc.Sql; -import org.springframework.test.context.jdbc.Sql.ExecutionPhase; - -import roomescape.common.exception.RoomescapeException; -import roomescape.member.business.MemberService; -import roomescape.member.infrastructure.persistence.MemberEntity; -import roomescape.member.infrastructure.persistence.MemberRepository; -import roomescape.member.infrastructure.persistence.Role; -import roomescape.reservation.infrastructure.persistence.ReservationEntity; -import roomescape.reservation.infrastructure.persistence.ReservationRepository; -import roomescape.reservation.infrastructure.persistence.ReservationStatus; -import roomescape.reservation.infrastructure.persistence.ReservationTimeEntity; -import roomescape.reservation.infrastructure.persistence.ReservationTimeRepository; -import roomescape.reservation.web.ReservationRequest; -import roomescape.reservation.web.ReservationResponse; -import roomescape.reservation.web.WaitingRequest; -import roomescape.theme.business.ThemeService; -import roomescape.theme.infrastructure.persistence.ThemeEntity; -import roomescape.theme.infrastructure.persistence.ThemeRepository; - -@SpringBootTest -@Sql(scripts = "/truncate.sql", executionPhase = ExecutionPhase.BEFORE_TEST_METHOD) -@Import({ReservationService.class, MemberService.class, ReservationTimeService.class, ThemeService.class}) -class ReservationServiceTest { - - @Autowired - ReservationTimeRepository reservationTimeRepository; - @Autowired - ReservationRepository reservationRepository; - @Autowired - ThemeRepository themeRepository; - @Autowired - MemberRepository memberRepository; - @Autowired - private ReservationService reservationService; - - @Test - @DisplayName("예약을 추가할때 이미 예약이 존재하면 예외가 발생한다.") - void reservationAlreadyExistFail() { - // given - ReservationTimeEntity reservationTime = reservationTimeRepository.save( - new ReservationTimeEntity(null, LocalTime.of(12, 30))); - ThemeEntity theme = themeRepository.save(new ThemeEntity(null, "테마명", "설명", "썸네일URL")); - MemberEntity member1 = memberRepository.save( - new MemberEntity(null, "name", "email@email.com", "password", Role.MEMBER)); - MemberEntity member2 = memberRepository.save( - new MemberEntity(null, "name2", "email2@email.com", "password", Role.MEMBER)); - LocalDate date = LocalDate.now().plusDays(1L); - - // when - reservationService.addReservation( - new ReservationRequest(date, reservationTime.getId(), theme.getId(), "paymentKey", "orderId", - 1000L, "paymentType"), member2.getId()); - - // then - assertThatThrownBy(() -> reservationService.addReservation( - new ReservationRequest(date, reservationTime.getId(), theme.getId(), "paymentKey", "orderId", - 1000L, "paymentType"), member1.getId())) - .isInstanceOf(RoomescapeException.class); - } - - @Test - @DisplayName("이미 예약한 멤버가 같은 테마에 대기를 신청하면 예외가 발생한다.") - void requestWaitWhenAlreadyReserveFail() { - // given - ReservationTimeEntity reservationTime = reservationTimeRepository.save( - new ReservationTimeEntity(null, LocalTime.of(12, 30))); - ThemeEntity theme = themeRepository.save(new ThemeEntity(null, "테마명", "설명", "썸네일URL")); - MemberEntity member = memberRepository.save( - new MemberEntity(null, "name", "email@email.com", "password", Role.MEMBER)); - LocalDate date = LocalDate.now().plusDays(1L); - - // when - reservationService.addReservation( - new ReservationRequest(date, reservationTime.getId(), theme.getId(), "paymentKey", "orderId", - 1000L, "paymentType"), member.getId()); - - // then - assertThatThrownBy(() -> reservationService.addWaiting( - new WaitingRequest(date, reservationTime.getId(), theme.getId()), member.getId())) - .isInstanceOf(RoomescapeException.class); - } - - @Test - @DisplayName("예약 대기를 두 번 이상 요청하면 예외가 발생한다.") - void requestWaitTwiceFail() { - // given - ReservationTimeEntity reservationTime = reservationTimeRepository.save( - new ReservationTimeEntity(null, LocalTime.of(12, 30))); - ThemeEntity theme = themeRepository.save(new ThemeEntity(null, "테마명", "설명", "썸네일URL")); - MemberEntity member = memberRepository.save( - new MemberEntity(null, "name", "email@email.com", "password", Role.MEMBER)); - MemberEntity member1 = memberRepository.save( - new MemberEntity(null, "name1", "email1@email.com", "password", Role.MEMBER)); - LocalDate date = LocalDate.now().plusDays(1L); - - // when - reservationService.addReservation( - new ReservationRequest(date, reservationTime.getId(), theme.getId(), "paymentKey", "orderId", - 1000L, "paymentType"), member.getId()); - - reservationService.addWaiting( - new WaitingRequest(date, reservationTime.getId(), theme.getId()), member1.getId()); - - // then - assertThatThrownBy(() -> reservationService.addWaiting( - new WaitingRequest(date, reservationTime.getId(), theme.getId()), member1.getId())) - .isInstanceOf(RoomescapeException.class); - } - - @Test - @DisplayName("이미 지난 날짜로 예약을 생성하면 예외가 발생한다.") - void beforeDateReservationFail() { - // given - ReservationTimeEntity reservationTime = reservationTimeRepository.save( - new ReservationTimeEntity(null, LocalTime.of(12, 30))); - ThemeEntity theme = themeRepository.save(new ThemeEntity(null, "테마명", "설명", "썸네일URL")); - MemberEntity member = memberRepository.save( - new MemberEntity(null, "name", "email@email.com", "password", Role.MEMBER)); - LocalDate beforeDate = LocalDate.now().minusDays(1L); - - // when & then - assertThatThrownBy(() -> reservationService.addReservation( - new ReservationRequest(beforeDate, reservationTime.getId(), theme.getId(), "paymentKey", "orderId", - 1000L, "paymentType"), member.getId())) - .isInstanceOf(RoomescapeException.class); - } - - @Test - @DisplayName("현재 날짜가 예약 당일이지만, 이미 지난 시간으로 예약을 생성하면 예외가 발생한다.") - void beforeTimeReservationFail() { - // given - LocalDateTime beforeTime = LocalDateTime.now().minusHours(1L).withNano(0); - ReservationTimeEntity reservationTime = reservationTimeRepository.save( - new ReservationTimeEntity(null, beforeTime.toLocalTime())); - ThemeEntity theme = themeRepository.save(new ThemeEntity(null, "테마명", "설명", "썸네일URL")); - MemberEntity member = memberRepository.save( - new MemberEntity(null, "name", "email@email.com", "password", Role.MEMBER)); - - // when & then - assertThatThrownBy(() -> reservationService.addReservation( - new ReservationRequest(beforeTime.toLocalDate(), reservationTime.getId(), theme.getId(), "paymentKey", - "orderId", 1000L, "paymentType"), member.getId())) - .isInstanceOf(RoomescapeException.class); - } - - @Test - @DisplayName("존재하지 않는 회원이 예약을 생성하려고 하면 예외가 발생한다.") - void notExistMemberReservationFail() { - // given - LocalDateTime beforeTime = LocalDateTime.now().minusDays(1L).withNano(0); - ReservationTimeEntity reservationTime = reservationTimeRepository.save( - new ReservationTimeEntity(null, beforeTime.toLocalTime())); - ThemeEntity theme = themeRepository.save(new ThemeEntity(null, "테마명", "설명", "썸네일URL")); - Long NotExistMemberId = 1L; - - // when & then - assertThatThrownBy(() -> reservationService.addReservation( - new ReservationRequest(beforeTime.toLocalDate(), reservationTime.getId(), theme.getId(), "paymentKey", - "orderId", 1000L, "paymentType"), - NotExistMemberId)) - .isInstanceOf(RoomescapeException.class); - } - - @Test - @DisplayName("예약을 조회할 때 종료 날짜가 시작 날짜 이전이면 예외가 발생한다.") - void invalidDateRange() { - // given - LocalDate dateFrom = LocalDate.now().plusDays(1); - LocalDate dateTo = LocalDate.now(); - - // when & then - assertThatThrownBy(() -> reservationService.findFilteredReservations(null, null, dateFrom, dateTo)) - .isInstanceOf(RoomescapeException.class); - } - - @Test - @DisplayName("대기중인 예약을 승인할 때, 기존에 예약이 존재하면 예외가 발생한다.") - void confirmWaitingWhenReservationExist() { - // given - ReservationTimeEntity reservationTime = reservationTimeRepository.save( - new ReservationTimeEntity(null, LocalTime.of(12, 30))); - ThemeEntity theme = themeRepository.save(new ThemeEntity(null, "테마명", "설명", "썸네일URL")); - MemberEntity admin = memberRepository.save( - new MemberEntity(null, "admin", "admin@email.com", "password", Role.ADMIN)); - MemberEntity member = memberRepository.save( - new MemberEntity(null, "name", "email@email.com", "password", Role.MEMBER)); - MemberEntity member1 = memberRepository.save( - new MemberEntity(null, "name1", "email1@email.com", "password", Role.MEMBER)); - - reservationService.addReservation( - new ReservationRequest(LocalDate.now().plusDays(1L), reservationTime.getId(), theme.getId(), - "paymentKey", "orderId", - 1000L, "paymentType"), member.getId()); - ReservationResponse waiting = reservationService.addWaiting( - new WaitingRequest(LocalDate.now().plusDays(1L), reservationTime.getId(), theme.getId()), - member1.getId()); - - // when & then - assertThatThrownBy(() -> reservationService.approveWaiting(waiting.id, admin.getId())) - .isInstanceOf(RoomescapeException.class); - } - - @Test - @DisplayName("대기중인 예약을 확정한다.") - void approveWaiting() { - // given - ReservationTimeEntity reservationTime = reservationTimeRepository.save( - new ReservationTimeEntity(null, LocalTime.of(12, 30))); - ThemeEntity theme = themeRepository.save(new ThemeEntity(null, "테마명", "설명", "썸네일URL")); - MemberEntity admin = memberRepository.save( - new MemberEntity(null, "admin", "admin@email.com", "password", Role.ADMIN)); - MemberEntity member = memberRepository.save( - new MemberEntity(null, "name", "email@email.com", "password", Role.MEMBER)); - - // when - ReservationResponse waiting = reservationService.addWaiting( - new WaitingRequest(LocalDate.now().plusDays(1L), reservationTime.getId(), theme.getId()), - member.getId()); - reservationService.approveWaiting(waiting.id, admin.getId()); - - // then - ReservationEntity confirmed = reservationRepository.findById(waiting.id).get(); - assertThat(confirmed.getReservationStatus()).isEqualTo(ReservationStatus.CONFIRMED_PAYMENT_REQUIRED); - } -} diff --git a/src/test/java/roomescape/reservation/business/ReservationServiteTest.kt b/src/test/java/roomescape/reservation/business/ReservationServiteTest.kt new file mode 100644 index 00000000..c9b2fa6a --- /dev/null +++ b/src/test/java/roomescape/reservation/business/ReservationServiteTest.kt @@ -0,0 +1,175 @@ +package roomescape.reservation.business + +import io.kotest.assertions.throwables.shouldThrow +import io.kotest.core.spec.style.FunSpec +import io.kotest.matchers.shouldBe +import io.mockk.every +import io.mockk.mockk +import roomescape.common.exception.ErrorType +import roomescape.common.exception.RoomescapeException +import roomescape.member.business.MemberService +import roomescape.member.infrastructure.persistence.Role +import roomescape.reservation.infrastructure.persistence.ReservationRepository +import roomescape.theme.business.ThemeService +import roomescape.util.MemberFixture +import roomescape.util.ReservationFixture +import roomescape.util.ReservationTimeFixture +import java.time.LocalDate +import java.time.LocalTime + +class ReservationServiteTest : FunSpec({ + + val reservationRepository: ReservationRepository = mockk() + val reservationTimeService: ReservationTimeService = mockk() + val memberService: MemberService = mockk() + val themeService: ThemeService = mockk() + val reservationService = ReservationService( + reservationRepository, + reservationTimeService, + memberService, + themeService + ) + + context("예약을 추가할 때") { + test("이미 예약이 있으면 예외를 던진다.") { + every { + reservationRepository.exists(any()) + } returns true + + val reservationRequest = ReservationFixture.createRequest() + + shouldThrow { + reservationService.addReservation(reservationRequest, 1L) + }.also { + it.errorType shouldBe ErrorType.RESERVATION_DUPLICATED + } + } + + context("날짜, 시간이 잘못 입력되면 예외를 던진다.") { + every { + reservationRepository.exists(any()) + } returns false + + every { + themeService.findThemeById(any()) + } returns mockk() + + every { + memberService.findById(any()) + } returns mockk() + + + test("지난 날짜이면 예외를 던진다.") { + val reservationRequest = ReservationFixture.createRequest().copy( + date = LocalDate.now().minusDays(1) + ) + + every { + reservationTimeService.findTimeById(any()) + } returns ReservationTimeFixture.create() + + shouldThrow { + reservationService.addReservation(reservationRequest, 1L) + }.also { + it.errorType shouldBe ErrorType.RESERVATION_PERIOD_IN_PAST + } + } + + test("지난 시간이면 예외를 던진다.") { + val reservationRequest = ReservationFixture.createRequest().copy( + date = LocalDate.now(), + ) + + every { + reservationTimeService.findTimeById(reservationRequest.timeId) + } returns ReservationTimeFixture.create( + startAt = LocalTime.now().minusMinutes(1) + ) + + shouldThrow { + reservationService.addReservation(reservationRequest, 1L) + }.also { + it.errorType shouldBe ErrorType.RESERVATION_PERIOD_IN_PAST + } + } + } + } + + context("예약 대기를 걸 때") { + test("이미 예약한 회원이 같은 날짜와 테마로 대기를 걸면 예외를 던진다.") { + val reservationRequest = ReservationFixture.createRequest().copy( + date = LocalDate.now(), + themeId = 1L, + timeId = 1L, + ) + + every { + reservationRepository.exists(any()) + } returns true + + shouldThrow { + val waitingRequest = ReservationFixture.createWaitingRequest( + date = reservationRequest.date, + themeId = reservationRequest.themeId, + timeId = reservationRequest.timeId + ) + reservationService.addWaiting(waitingRequest, 1L) + }.also { + it.errorType shouldBe ErrorType.HAS_RESERVATION_OR_WAITING + } + } + } + + context("예약을 조회할 때") { + test("종료 날짜가 시작 날짜보다 이전이면 예외를 던진다.") { + val startFrom = LocalDate.now() + val endAt = startFrom.minusDays(1) + + shouldThrow { + reservationService.findFilteredReservations( + null, + null, + startFrom, + endAt + ) + }.also { + it.errorType shouldBe ErrorType.INVALID_DATE_RANGE + } + } + } + + context("대기중인 예약을 승인할 때") { + test("관리자가 아니면 예외를 던진다.") { + val member = MemberFixture.create(id = 1L, role = Role.MEMBER) + + every { + memberService.findById(any()) + } returns member + + shouldThrow { + reservationService.approveWaiting(1L, member.id!!) + }.also { + it.errorType shouldBe ErrorType.PERMISSION_DOES_NOT_EXIST + } + } + + test("이미 확정된 예약이 있으면 예외를 던진다.") { + val member = MemberFixture.create(id = 1L, role = Role.ADMIN) + val reservationId = 1L + + every { + memberService.findById(any()) + } returns member + + every { + reservationRepository.isExistConfirmedReservation(reservationId) + } returns true + + shouldThrow { + reservationService.approveWaiting(reservationId, member.id!!) + }.also { + it.errorType shouldBe ErrorType.RESERVATION_DUPLICATED + } + } + } +}) diff --git a/src/test/java/roomescape/util/Fixtures.kt b/src/test/java/roomescape/util/Fixtures.kt index 7b6c3026..d1009093 100644 --- a/src/test/java/roomescape/util/Fixtures.kt +++ b/src/test/java/roomescape/util/Fixtures.kt @@ -11,6 +11,8 @@ import roomescape.payment.web.PaymentCancel import roomescape.reservation.infrastructure.persistence.ReservationEntity import roomescape.reservation.infrastructure.persistence.ReservationStatus import roomescape.reservation.infrastructure.persistence.ReservationTimeEntity +import roomescape.reservation.web.ReservationRequest +import roomescape.reservation.web.WaitingRequest import roomescape.theme.infrastructure.persistence.ThemeEntity import java.time.LocalDate import java.time.LocalTime @@ -76,6 +78,34 @@ object ReservationFixture { member: MemberEntity = MemberFixture.create(), status: ReservationStatus = ReservationStatus.CONFIRMED_PAYMENT_REQUIRED ): ReservationEntity = ReservationEntity(id, date, reservationTime, theme, member, status) + + fun createRequest( + date: LocalDate = LocalDate.now().plusWeeks(1), + themeId: Long = 1L, + timeId: Long = 1L, + paymentKey: String = "paymentKey", + orderId: String = "orderId", + amount: Long = 10000L, + paymentType: String = "NORMAL", + ): ReservationRequest = ReservationRequest( + date = date, + timeId = timeId, + themeId = themeId, + paymentKey = paymentKey, + orderId = orderId, + amount = amount, + paymentType = paymentType + ) + + fun createWaitingRequest( + date: LocalDate = LocalDate.now().plusWeeks(1), + themeId: Long = 1L, + timeId: Long = 1L + ): WaitingRequest = WaitingRequest( + date = date, + timeId = timeId, + themeId = themeId + ) } object JwtFixture {