package roomescape.reservation.implement import io.kotest.assertions.throwables.shouldNotThrow 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 org.springframework.data.jpa.domain.Specification import roomescape.member.infrastructure.persistence.Role import roomescape.reservation.exception.ReservationErrorCode import roomescape.reservation.exception.ReservationException import roomescape.reservation.infrastructure.persistence.ReservationEntity import roomescape.reservation.infrastructure.persistence.ReservationRepository import roomescape.reservation.infrastructure.persistence.ReservationStatus import roomescape.util.MemberFixture import roomescape.util.ReservationFixture import roomescape.util.ThemeFixture import roomescape.util.TimeFixture import java.time.LocalDate import java.time.LocalTime class ReservationValidatorTest : FunSpec({ val reservationRepository: ReservationRepository = mockk() val reservationValidator = ReservationValidator(reservationRepository) context("validateIsNotPast") { val today = LocalDate.now() val now = LocalTime.now() test("입력된 날짜가 오늘 이전이면 예외를 던진다.") { val requestDate = today.minusDays(1) shouldThrow { reservationValidator.validateIsPast(requestDate, now) }.also { it.errorCode shouldBe ReservationErrorCode.PAST_REQUEST_DATETIME } } test("오늘 날짜라도 시간이 지났다면 예외를 던진다.") { val requestTime = now.minusMinutes(1) shouldThrow { reservationValidator.validateIsPast(today, requestTime) }.also { it.errorCode shouldBe ReservationErrorCode.PAST_REQUEST_DATETIME } } } context("validateSearchDateRange") { test("시작 날짜만 입력되면 종료한다.") { shouldNotThrow { reservationValidator.validateSearchDateRange(LocalDate.now(), null) } } test("종료 날짜만 입력되면 종료한다.") { shouldNotThrow { reservationValidator.validateSearchDateRange(null, LocalDate.now()) } } test("두 날짜가 같으면 종료한다.") { shouldNotThrow { reservationValidator.validateSearchDateRange(LocalDate.now(), LocalDate.now()) } } test("종료 날짜가 시작 날짜 이전이면 예외를 던진다.") { shouldThrow { reservationValidator.validateSearchDateRange(LocalDate.now(), LocalDate.now().minusDays(1)) }.also { it.errorCode shouldBe ReservationErrorCode.INVALID_SEARCH_DATE_RANGE } } } context("validateIsAlreadyExists") { test("동일한 날짜, 시간, 테마를 가지는 예약이 있으면 예외를 던진다.") { every { reservationRepository.exists(any>()) } returns true shouldThrow { reservationValidator.validateIsAlreadyExists( LocalDate.now(), TimeFixture.create(), ThemeFixture.create() ) }.also { it.errorCode shouldBe ReservationErrorCode.RESERVATION_DUPLICATED } } } context("validateMemberAlreadyReserve") { test("회원이 동일한 날짜, 시간, 테마인 예약(대기)를 이미 했다면 예외를 던진다.") { every { reservationRepository.exists(any>()) } returns true shouldThrow { reservationValidator.validateMemberAlreadyReserve(1L, 1L, LocalDate.now(), 1L) }.also { it.errorCode shouldBe ReservationErrorCode.ALREADY_RESERVE } } } context("validateIsWaiting") { test("예약 상태가 WAITING이 아니면 예외를 던진다.") { ReservationStatus.confirmedStatus().forEach { status -> shouldThrow { val reservation = ReservationFixture.create(status = status) reservationValidator.validateIsWaiting(reservation) }.also { it.errorCode shouldBe ReservationErrorCode.ALREADY_CONFIRMED } } } } context("validateCreateAuthority") { test("관리자가 아니면 예외를 던진다.") { shouldThrow { reservationValidator.validateCreateAuthority(MemberFixture.user()) }.also { it.errorCode shouldBe ReservationErrorCode.NO_PERMISSION } } } context("validateDeleteAuthority") { test("입력된 회원이 관리자이면 종료한다.") { shouldNotThrow { reservationValidator.validateDeleteAuthority(mockk(), MemberFixture.admin()) } } test("입력된 회원이 관리자가 아니고, 예약한 회원과 다른 회원이면 예외를 던진다.") { shouldThrow { reservationValidator.validateDeleteAuthority( ReservationFixture.create(member = MemberFixture.create(id = 1L)), MemberFixture.create(id = 2L, role = Role.MEMBER) ) }.also { it.errorCode shouldBe ReservationErrorCode.NOT_RESERVATION_OWNER } } } context("validateAlreadyConfirmed") { val reservationId = 1L test("입력된 ID의 예약과 동일한 날짜, 시간, 테마를 가지는 다른 확정 예약이 있으면 예외를 던진다.") { every { reservationRepository.isExistConfirmedReservation(reservationId) } returns true shouldThrow { reservationValidator.validateAlreadyConfirmed(reservationId) }.also { it.errorCode shouldBe ReservationErrorCode.CONFIRMED_RESERVATION_ALREADY_EXISTS } } } })