171 lines
6.5 KiB
Kotlin

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<ReservationException> {
reservationValidator.validateIsPast(requestDate, now)
}.also {
it.errorCode shouldBe ReservationErrorCode.PAST_REQUEST_DATETIME
}
}
test("오늘 날짜라도 시간이 지났다면 예외를 던진다.") {
val requestTime = now.minusMinutes(1)
shouldThrow<ReservationException> {
reservationValidator.validateIsPast(today, requestTime)
}.also {
it.errorCode shouldBe ReservationErrorCode.PAST_REQUEST_DATETIME
}
}
}
context("validateSearchDateRange") {
test("시작 날짜만 입력되면 종료한다.") {
shouldNotThrow<ReservationException> {
reservationValidator.validateSearchDateRange(LocalDate.now(), null)
}
}
test("종료 날짜만 입력되면 종료한다.") {
shouldNotThrow<ReservationException> {
reservationValidator.validateSearchDateRange(null, LocalDate.now())
}
}
test("두 날짜가 같으면 종료한다.") {
shouldNotThrow<ReservationException> {
reservationValidator.validateSearchDateRange(LocalDate.now(), LocalDate.now())
}
}
test("종료 날짜가 시작 날짜 이전이면 예외를 던진다.") {
shouldThrow<ReservationException> {
reservationValidator.validateSearchDateRange(LocalDate.now(), LocalDate.now().minusDays(1))
}.also {
it.errorCode shouldBe ReservationErrorCode.INVALID_SEARCH_DATE_RANGE
}
}
}
context("validateIsAlreadyExists") {
test("동일한 날짜, 시간, 테마를 가지는 예약이 있으면 예외를 던진다.") {
every {
reservationRepository.exists(any<Specification<ReservationEntity>>())
} returns true
shouldThrow<ReservationException> {
reservationValidator.validateIsAlreadyExists(
LocalDate.now(),
TimeFixture.create(),
ThemeFixture.create()
)
}.also {
it.errorCode shouldBe ReservationErrorCode.RESERVATION_DUPLICATED
}
}
}
context("validateMemberAlreadyReserve") {
test("회원이 동일한 날짜, 시간, 테마인 예약(대기)를 이미 했다면 예외를 던진다.") {
every {
reservationRepository.exists(any<Specification<ReservationEntity>>())
} returns true
shouldThrow<ReservationException> {
reservationValidator.validateMemberAlreadyReserve(1L, 1L, LocalDate.now(), 1L)
}.also {
it.errorCode shouldBe ReservationErrorCode.ALREADY_RESERVE
}
}
}
context("validateIsWaiting") {
test("예약 상태가 WAITING이 아니면 예외를 던진다.") {
ReservationStatus.confirmedStatus().forEach { status ->
shouldThrow<ReservationException> {
val reservation = ReservationFixture.create(status = status)
reservationValidator.validateIsWaiting(reservation)
}.also {
it.errorCode shouldBe ReservationErrorCode.ALREADY_CONFIRMED
}
}
}
}
context("validateCreateAuthority") {
test("관리자가 아니면 예외를 던진다.") {
shouldThrow<ReservationException> {
reservationValidator.validateCreateAuthority(MemberFixture.user())
}.also {
it.errorCode shouldBe ReservationErrorCode.NO_PERMISSION
}
}
}
context("validateDeleteAuthority") {
test("입력된 회원이 관리자이면 종료한다.") {
shouldNotThrow<ReservationException> {
reservationValidator.validateDeleteAuthority(mockk(), MemberFixture.admin())
}
}
test("입력된 회원이 관리자가 아니고, 예약한 회원과 다른 회원이면 예외를 던진다.") {
shouldThrow<ReservationException> {
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<ReservationException> {
reservationValidator.validateAlreadyConfirmed(reservationId)
}.also {
it.errorCode shouldBe ReservationErrorCode.CONFIRMED_RESERVATION_ALREADY_EXISTS
}
}
}
})