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 } } } })