package roomescape.reservation.implement 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.member.exception.MemberErrorCode import roomescape.member.exception.MemberException import roomescape.member.implement.MemberFinder 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.theme.exception.ThemeErrorCode import roomescape.theme.exception.ThemeException import roomescape.theme.implement.ThemeFinder import roomescape.time.exception.TimeErrorCode import roomescape.time.exception.TimeException import roomescape.time.implement.TimeFinder import roomescape.util.MemberFixture import roomescape.util.ThemeFixture import roomescape.util.TimeFixture import roomescape.util.TsidFactory import java.time.LocalDate import java.time.LocalTime class ReservationWriterTest : FunSpec({ val reservationValidator: ReservationValidator = mockk() val reservationRepository: ReservationRepository = mockk() val memberFinder: MemberFinder = mockk() val timeFinder: TimeFinder = mockk() val themeFinder: ThemeFinder = mockk() val reservationWriter = ReservationWriter( reservationValidator, reservationRepository, memberFinder, timeFinder, themeFinder, TsidFactory ) context("create") { val today = LocalDate.now() val timeId = 1L val themeId = 1L val memberId = 1L val status = ReservationStatus.CONFIRMED val requesterId = 1L test("시간을 찾을 수 없으면 실패한다.") { every { timeFinder.findById(any()) } throws TimeException(TimeErrorCode.TIME_NOT_FOUND) shouldThrow { reservationWriter.create(today, timeId, themeId, memberId, status, requesterId) }.also { it.errorCode shouldBe TimeErrorCode.TIME_NOT_FOUND } } test("이전 날짜이면 실패한다.") { every { timeFinder.findById(timeId) } returns TimeFixture.create(id = timeId, startAt = LocalTime.now().plusHours(1)) every { reservationValidator.validateIsPast(any(), any()) } throws ReservationException(ReservationErrorCode.PAST_REQUEST_DATETIME) shouldThrow { reservationWriter.create(today.minusDays(1), timeId, themeId, memberId, status, requesterId) }.also { it.errorCode shouldBe ReservationErrorCode.PAST_REQUEST_DATETIME } } test("테마를 찾을 수 없으면 실패한다.") { every { timeFinder.findById(timeId) } returns TimeFixture.create(id = timeId) every { reservationValidator.validateIsPast(any(), any()) } returns Unit every { themeFinder.findById(themeId) } throws ThemeException(ThemeErrorCode.THEME_NOT_FOUND) shouldThrow { reservationWriter.create(today.plusDays(1), timeId, themeId, memberId, status, requesterId) }.also { it.errorCode shouldBe ThemeErrorCode.THEME_NOT_FOUND } } test("회원을 찾을 수 없으면 실패한다.") { every { timeFinder.findById(timeId) } returns TimeFixture.create(id = timeId) every { reservationValidator.validateIsPast(any(), any()) } returns Unit every { themeFinder.findById(themeId) } returns ThemeFixture.create(id = themeId) every { memberFinder.findById(memberId) } throws MemberException(MemberErrorCode.MEMBER_NOT_FOUND) shouldThrow { reservationWriter.create(today.plusDays(1), timeId, themeId, memberId, status, requesterId) }.also { it.errorCode shouldBe MemberErrorCode.MEMBER_NOT_FOUND } } test("이미 예약이 있는 회원이 대기를 추가하면 실패한다.") { every { timeFinder.findById(timeId) } returns TimeFixture.create(id = timeId) every { reservationValidator.validateIsPast(any(), any()) } returns Unit every { themeFinder.findById(themeId) } returns ThemeFixture.create(id = themeId) every { memberFinder.findById(memberId) } returns MemberFixture.create(id = memberId) every { reservationValidator.validateMemberAlreadyReserve(themeId, timeId, today, memberId) } throws ReservationException(ReservationErrorCode.ALREADY_RESERVE) shouldThrow { reservationWriter.create(today, timeId, themeId, memberId, status = ReservationStatus.WAITING, memberId) }.also { it.errorCode shouldBe ReservationErrorCode.ALREADY_RESERVE } } test("동일한 날짜, 시간, 테마인 예약이 이미 있으면 실패한다.") { every { timeFinder.findById(timeId) } returns TimeFixture.create(id = timeId) every { reservationValidator.validateIsPast(any(), any()) } returns Unit every { themeFinder.findById(themeId) } returns ThemeFixture.create(id = themeId) every { memberFinder.findById(memberId) } returns MemberFixture.create(id = memberId) every { reservationValidator.validateIsAlreadyExists(today, any(), any()) } throws ReservationException(ReservationErrorCode.RESERVATION_DUPLICATED) shouldThrow { reservationWriter.create(today, timeId, themeId, memberId, status, memberId) }.also { it.errorCode shouldBe ReservationErrorCode.RESERVATION_DUPLICATED } } test("예약하려는 회원과 신청한 회원이 다를 때, 신청한 회원을 찾을 수 없으면 실패한다.") { every { timeFinder.findById(timeId) } returns TimeFixture.create(id = timeId) every { reservationValidator.validateIsPast(any(), any()) } returns Unit every { themeFinder.findById(themeId) } returns ThemeFixture.create(id = themeId) every { memberFinder.findById(memberId) } returns MemberFixture.create(id = memberId) every { reservationValidator.validateIsAlreadyExists(today, any(), any()) } returns Unit every { memberFinder.findById(memberId + 1) } throws MemberException(MemberErrorCode.MEMBER_NOT_FOUND) shouldThrow { reservationWriter.create( today, timeId, themeId, memberId = memberId, status, requesterId = (memberId + 1) ) }.also { it.errorCode shouldBe MemberErrorCode.MEMBER_NOT_FOUND } } test("예약하려는 회원과 신청한 회원이 다를 때, 신청한 회원이 관리자가 아니면 실패한다.") { every { timeFinder.findById(timeId) } returns TimeFixture.create(id = timeId) every { reservationValidator.validateIsPast(any(), any()) } returns Unit every { themeFinder.findById(themeId) } returns ThemeFixture.create(id = themeId) every { memberFinder.findById(memberId) } returns MemberFixture.create(id = memberId) every { reservationValidator.validateIsAlreadyExists(today, any(), any()) } returns Unit every { memberFinder.findById(memberId + 1) } returns MemberFixture.create(id = memberId + 1) every { reservationValidator.validateCreateAuthority(any()) } throws ReservationException(ReservationErrorCode.NO_PERMISSION) shouldThrow { reservationWriter.create( today, timeId, themeId, memberId = memberId, status, requesterId = (memberId + 1) ) }.also { it.errorCode shouldBe ReservationErrorCode.NO_PERMISSION } } } context("deleteWaiting") { val reservation: ReservationEntity = mockk() val requesterId = 1L test("대기 상태가 아니면 실패한다.") { every { reservationValidator.validateIsWaiting(any()) } throws ReservationException(ReservationErrorCode.ALREADY_CONFIRMED) shouldThrow { reservationWriter.deleteWaiting(reservation, requesterId) }.also { it.errorCode shouldBe ReservationErrorCode.ALREADY_CONFIRMED } } test("삭제하려는 회원이 관리자가 아니고, 예약한 회원과 다르면 실패한다.") { every { reservationValidator.validateIsWaiting(any()) } returns Unit every { reservationValidator.validateDeleteAuthority(any(), any()) } throws ReservationException(ReservationErrorCode.NOT_RESERVATION_OWNER) shouldThrow { reservationWriter.deleteWaiting(reservation, requesterId) }.also { it.errorCode shouldBe ReservationErrorCode.NOT_RESERVATION_OWNER } } } context("deleteConfirm") { val reservation: ReservationEntity = mockk() val requesterId = 1L test("삭제하려는 회원이 관리자가 아니고, 예약한 회원과 다르면 실패한다.") { every { reservationValidator.validateIsWaiting(any()) } returns Unit every { reservationValidator.validateDeleteAuthority(any(), any()) } throws ReservationException(ReservationErrorCode.NOT_RESERVATION_OWNER) shouldThrow { reservationWriter.deleteConfirmed(reservation, requesterId) }.also { it.errorCode shouldBe ReservationErrorCode.NOT_RESERVATION_OWNER } } } context("confirm") { val reservationId = 1L test("승인하려는 대기와 같은 날짜,시간,테마를 가진 확정 예약이 있으면 실패한다.") { every { reservationValidator.validateAlreadyConfirmed(reservationId) } throws ReservationException(ReservationErrorCode.CONFIRMED_RESERVATION_ALREADY_EXISTS) shouldThrow { reservationWriter.confirm(reservationId) }.also { it.errorCode shouldBe ReservationErrorCode.CONFIRMED_RESERVATION_ALREADY_EXISTS } } } })