generated from pricelees/issue-pr-template
feat: 통합 테스트 전환 & API 기능 변경으로 인한 기존 테스트 제거
This commit is contained in:
parent
11fd345d5e
commit
e7f69aaee4
@ -1,194 +0,0 @@
|
||||
package roomescape.payment.infrastructure.client
|
||||
|
||||
import roomescape.payment.web.PaymentCancelRequest
|
||||
import kotlin.math.roundToLong
|
||||
|
||||
object SampleTossPaymentConst {
|
||||
val paymentKey: String = "5EnNZRJGvaBX7zk2yd8ydw26XvwXkLrx9POLqKQjmAw4b0e1"
|
||||
|
||||
val orderId: String = "MC4wODU4ODQwMzg4NDk0"
|
||||
|
||||
val amount: Long = 1000L
|
||||
|
||||
val paymentType: String = "카드"
|
||||
|
||||
val cancelReason: String = "테스트 결제 취소"
|
||||
|
||||
val paymentRequest: PaymentApproveRequest = PaymentApproveRequest(
|
||||
paymentKey,
|
||||
orderId,
|
||||
amount,
|
||||
paymentType
|
||||
)
|
||||
|
||||
val paymentRequestJson: String = """
|
||||
{
|
||||
"paymentKey": "$paymentKey",
|
||||
"orderId": "$orderId",
|
||||
"amount": $amount,
|
||||
"paymentType": "$paymentType"
|
||||
}
|
||||
""".trimIndent()
|
||||
|
||||
val cancelRequest: PaymentCancelRequest = PaymentCancelRequest(
|
||||
paymentKey,
|
||||
amount,
|
||||
cancelReason
|
||||
)
|
||||
|
||||
val cancelRequestJson: String = """
|
||||
{
|
||||
"cancelReason": "$cancelReason"
|
||||
}
|
||||
""".trimIndent()
|
||||
|
||||
val tossPaymentErrorJson: String = """
|
||||
{
|
||||
"code": "ERROR_CODE",
|
||||
"message": "Error message"
|
||||
}
|
||||
""".trimIndent()
|
||||
|
||||
val confirmJson: String = """
|
||||
{
|
||||
"mId": "tosspayments",
|
||||
"lastTransactionKey": "9C62B18EEF0DE3EB7F4422EB6D14BC6E",
|
||||
"paymentKey": "$paymentKey",
|
||||
"orderId": "$orderId",
|
||||
"orderName": "토스 티셔츠 외 2건",
|
||||
"taxExemptionAmount": 0,
|
||||
"status": "DONE",
|
||||
"requestedAt": "2024-02-13T12:17:57+09:00",
|
||||
"approvedAt": "2024-02-13T12:18:14+09:00",
|
||||
"useEscrow": false,
|
||||
"cultureExpense": false,
|
||||
"card": {
|
||||
"issuerCode": "71",
|
||||
"acquirerCode": "71",
|
||||
"number": "12345678****000*",
|
||||
"installmentPlanMonths": 0,
|
||||
"isInterestFree": false,
|
||||
"interestPayer": null,
|
||||
"approveNo": "00000000",
|
||||
"useCardPoint": false,
|
||||
"cardType": "신용",
|
||||
"ownerType": "개인",
|
||||
"acquireStatus": "READY",
|
||||
"receiptUrl": "https://dashboard.tosspayments.com/receipt/redirection?transactionId=tviva20240213121757MvuS8&ref=PX",
|
||||
"amount": 1000
|
||||
},
|
||||
"virtualAccount": null,
|
||||
"transfer": null,
|
||||
"mobilePhone": null,
|
||||
"giftCertificate": null,
|
||||
"cashReceipt": null,
|
||||
"cashReceipts": null,
|
||||
"discount": null,
|
||||
"cancels": null,
|
||||
"secret": null,
|
||||
"type": "NORMAL",
|
||||
"easyPay": {
|
||||
"provider": "토스페이",
|
||||
"amount": 0,
|
||||
"discountAmount": 0
|
||||
},
|
||||
"easyPayAmount": 0,
|
||||
"easyPayDiscountAmount": 0,
|
||||
"country": "KR",
|
||||
"failure": null,
|
||||
"isPartialCancelable": true,
|
||||
"receipt": {
|
||||
"url": "https://dashboard.tosspayments.com/receipt/redirection?transactionId=tviva20240213121757MvuS8&ref=PX"
|
||||
},
|
||||
"checkout": {
|
||||
"url": "https://api.tosspayments.com/v1/payments/5EnNZRJGvaBX7zk2yd8ydw26XvwXkLrx9POLqKQjmAw4b0e1/checkout"
|
||||
},
|
||||
"currency": "KRW",
|
||||
"totalAmount": $amount,
|
||||
"balanceAmount": $amount,
|
||||
"suppliedAmount": ${(amount / 1.1).roundToLong()},
|
||||
"vat": ${amount - (amount / 1.1).roundToLong()},
|
||||
"taxFreeAmount": 0,
|
||||
"method": "$paymentType",
|
||||
"version": "2022-11-16"
|
||||
}
|
||||
""".trimIndent()
|
||||
|
||||
val cancelJson: String = """
|
||||
{
|
||||
"mId": "tosspayments",
|
||||
"lastTransactionKey": "090A796806E726BBB929F4A2CA7DB9A7",
|
||||
"paymentKey": "$paymentKey",
|
||||
"orderId": "$orderId",
|
||||
"orderName": "토스 티셔츠 외 2건",
|
||||
"taxExemptionAmount": 0,
|
||||
"status": "CANCELED",
|
||||
"requestedAt": "2024-02-13T12:17:57+09:00",
|
||||
"approvedAt": "2024-02-13T12:18:14+09:00",
|
||||
"useEscrow": false,
|
||||
"cultureExpense": false,
|
||||
"card": {
|
||||
"issuerCode": "71",
|
||||
"acquirerCode": "71",
|
||||
"number": "12345678****000*",
|
||||
"installmentPlanMonths": 0,
|
||||
"isInterestFree": false,
|
||||
"interestPayer": null,
|
||||
"approveNo": "00000000",
|
||||
"useCardPoint": false,
|
||||
"cardType": "신용",
|
||||
"ownerType": "개인",
|
||||
"acquireStatus": "READY",
|
||||
"amount": 1000
|
||||
},
|
||||
"virtualAccount": null,
|
||||
"transfer": null,
|
||||
"mobilePhone": null,
|
||||
"giftCertificate": null,
|
||||
"cashReceipt": null,
|
||||
"cashReceipts": null,
|
||||
"discount": null,
|
||||
"cancels": [
|
||||
{
|
||||
"transactionKey": "090A796806E726BBB929F4A2CA7DB9A7",
|
||||
"cancelReason": "$cancelReason",
|
||||
"taxExemptionAmount": 0,
|
||||
"canceledAt": "2024-02-13T12:20:23+09:00",
|
||||
"easyPayDiscountAmount": 0,
|
||||
"receiptKey": null,
|
||||
"cancelAmount": $amount,
|
||||
"taxFreeAmount": 0,
|
||||
"refundableAmount": 0,
|
||||
"cancelStatus": "DONE",
|
||||
"cancelRequestId": null
|
||||
}
|
||||
],
|
||||
"secret": null,
|
||||
"type": "NORMAL",
|
||||
"easyPay": {
|
||||
"provider": "토스페이",
|
||||
"amount": 0,
|
||||
"discountAmount": 0
|
||||
},
|
||||
"easyPayAmount": 0,
|
||||
"easyPayDiscountAmount": 0,
|
||||
"country": "KR",
|
||||
"failure": null,
|
||||
"isPartialCancelable": true,
|
||||
"receipt": {
|
||||
"url": "https://dashboard.tosspayments.com/receipt/redirection?transactionId=tviva20240213121757MvuS8&ref=PX"
|
||||
},
|
||||
"checkout": {
|
||||
"url": "https://api.tosspayments.com/v1/payments/5EnNZRJGvaBX7zk2yd8ydw26XvwXkLrx9POLqKQjmAw4b0e1/checkout"
|
||||
},
|
||||
"currency": "KRW",
|
||||
"totalAmount": $amount,
|
||||
"balanceAmount": 0,
|
||||
"suppliedAmount": 0,
|
||||
"vat": 0,
|
||||
"taxFreeAmount": 0,
|
||||
"method": "$paymentType",
|
||||
"version": "2022-11-16"
|
||||
}
|
||||
""".trimIndent()
|
||||
}
|
||||
@ -1,284 +0,0 @@
|
||||
package roomescape.reservation.business
|
||||
|
||||
import io.kotest.assertions.assertSoftly
|
||||
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.*
|
||||
import roomescape.reservation.exception.ReservationErrorCode
|
||||
import roomescape.reservation.exception.ReservationException
|
||||
import roomescape.reservation.implement.ReservationFinder
|
||||
import roomescape.reservation.implement.ReservationWriter
|
||||
import roomescape.reservation.infrastructure.persistence.ReservationStatus
|
||||
import roomescape.util.MemberFixture
|
||||
import roomescape.util.ReservationFixture
|
||||
import roomescape.util.ThemeFixture
|
||||
import roomescape.util.TimeFixture
|
||||
|
||||
class ReservationCommandServiceTest : FunSpec({
|
||||
|
||||
val reservationFinder: ReservationFinder = mockk()
|
||||
val reservationWriter: ReservationWriter = mockk()
|
||||
val reservationWriteService = ReservationWriteService(reservationFinder, reservationWriter)
|
||||
|
||||
context("createReservationWithPayment") {
|
||||
val request = ReservationFixture.createRequest()
|
||||
val memberId = 1L
|
||||
|
||||
test("정상 응답") {
|
||||
val createdReservation = ReservationFixture.create(
|
||||
date = request.date,
|
||||
time = TimeFixture.create(id = request.timeId),
|
||||
theme = ThemeFixture.create(id = request.themeId),
|
||||
member = MemberFixture.create(id = memberId),
|
||||
status = ReservationStatus.CONFIRMED
|
||||
)
|
||||
|
||||
every {
|
||||
reservationWriter.create(
|
||||
date = request.date,
|
||||
timeId = request.timeId,
|
||||
themeId = request.themeId,
|
||||
status = ReservationStatus.CONFIRMED,
|
||||
memberId = memberId,
|
||||
requesterId = memberId
|
||||
)
|
||||
} returns createdReservation
|
||||
|
||||
val result = reservationWriteService.createReservationWithPayment(request, memberId)
|
||||
|
||||
assertSoftly(result) {
|
||||
this.date shouldBe request.date
|
||||
this.time.id shouldBe request.timeId
|
||||
this.theme.id shouldBe request.themeId
|
||||
this.member.id shouldBe memberId
|
||||
this.status shouldBe ReservationStatus.CONFIRMED
|
||||
}
|
||||
}
|
||||
|
||||
test("예약 생성에 실패하면 예외 응답") {
|
||||
every {
|
||||
reservationWriter.create(any(), any(), any(), any(), any(), any())
|
||||
} throws ReservationException(ReservationErrorCode.RESERVATION_DUPLICATED)
|
||||
|
||||
shouldThrow<ReservationException> {
|
||||
reservationWriteService.createReservationWithPayment(request, memberId)
|
||||
}.also {
|
||||
it.errorCode shouldBe ReservationErrorCode.RESERVATION_DUPLICATED
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
context("createReservationByAdmin") {
|
||||
val request = ReservationFixture.createAdminRequest()
|
||||
val adminId = request.memberId + 1
|
||||
|
||||
test("정상 응답") {
|
||||
val createdReservation = ReservationFixture.create(
|
||||
date = request.date,
|
||||
time = TimeFixture.create(id = request.timeId),
|
||||
theme = ThemeFixture.create(id = request.themeId),
|
||||
member = MemberFixture.create(id = request.memberId),
|
||||
status = ReservationStatus.CONFIRMED_PAYMENT_REQUIRED
|
||||
)
|
||||
|
||||
every {
|
||||
reservationWriter.create(
|
||||
date = request.date,
|
||||
timeId = request.timeId,
|
||||
themeId = request.themeId,
|
||||
status = ReservationStatus.CONFIRMED_PAYMENT_REQUIRED,
|
||||
memberId = request.memberId,
|
||||
requesterId = adminId
|
||||
)
|
||||
} returns createdReservation
|
||||
|
||||
val response = reservationWriteService.createReservationByAdmin(request, adminId)
|
||||
|
||||
assertSoftly(response) {
|
||||
this.date shouldBe request.date
|
||||
this.time.id shouldBe request.timeId
|
||||
this.theme.id shouldBe request.themeId
|
||||
this.member.id shouldBe request.memberId
|
||||
this.status shouldBe ReservationStatus.CONFIRMED_PAYMENT_REQUIRED
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
context("createWaiting") {
|
||||
val request = ReservationFixture.createWaitingRequest()
|
||||
val memberId = 1L
|
||||
|
||||
test("정상 응답") {
|
||||
val createdWaiting = ReservationFixture.create(
|
||||
date = request.date,
|
||||
time = TimeFixture.create(id = request.timeId),
|
||||
theme = ThemeFixture.create(id = request.themeId),
|
||||
member = MemberFixture.create(id = memberId),
|
||||
status = ReservationStatus.WAITING
|
||||
)
|
||||
|
||||
every {
|
||||
reservationWriter.create(
|
||||
date = request.date,
|
||||
timeId = request.timeId,
|
||||
themeId = request.themeId,
|
||||
status = ReservationStatus.WAITING,
|
||||
memberId = memberId,
|
||||
requesterId = memberId
|
||||
)
|
||||
} returns createdWaiting
|
||||
|
||||
val response = reservationWriteService.createWaiting(request, memberId)
|
||||
|
||||
assertSoftly(response) {
|
||||
this.date shouldBe request.date
|
||||
this.time.id shouldBe request.timeId
|
||||
this.theme.id shouldBe request.themeId
|
||||
this.member.id shouldBe memberId
|
||||
this.status shouldBe ReservationStatus.WAITING
|
||||
}
|
||||
}
|
||||
|
||||
test("이미 예약한 내역이 있으면 예외 응답") {
|
||||
every {
|
||||
reservationWriter.create(any(), any(), any(), any(), any(), any())
|
||||
} throws ReservationException(ReservationErrorCode.ALREADY_RESERVE)
|
||||
|
||||
shouldThrow<ReservationException> {
|
||||
reservationWriteService.createWaiting(request, memberId)
|
||||
}.also {
|
||||
it.errorCode shouldBe ReservationErrorCode.ALREADY_RESERVE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
context("deleteReservation") {
|
||||
val reservationId = 1L
|
||||
val memberId = 1L
|
||||
val reservation = ReservationFixture.create(id = reservationId, member = MemberFixture.create(id = memberId))
|
||||
|
||||
test("정상 응답") {
|
||||
every { reservationFinder.findById(reservationId) } returns reservation
|
||||
every { reservationWriter.deleteConfirmed(reservation, memberId) } just Runs
|
||||
|
||||
shouldNotThrow<Exception> {
|
||||
reservationWriteService.deleteReservation(reservationId, memberId)
|
||||
}
|
||||
|
||||
verify(exactly = 1) { reservationWriter.deleteConfirmed(reservation, memberId) }
|
||||
}
|
||||
|
||||
test("예약을 찾을 수 없으면 예외 응답") {
|
||||
every {
|
||||
reservationFinder.findById(reservationId)
|
||||
} throws ReservationException(ReservationErrorCode.RESERVATION_NOT_FOUND)
|
||||
|
||||
shouldThrow<ReservationException> {
|
||||
reservationWriteService.deleteReservation(reservationId, memberId)
|
||||
}.also {
|
||||
it.errorCode shouldBe ReservationErrorCode.RESERVATION_NOT_FOUND
|
||||
}
|
||||
}
|
||||
|
||||
test("삭제하려는 회원이 관리자가 아니고, 대기한 회원과 다르면 예외 응답") {
|
||||
every { reservationFinder.findById(reservationId) } returns reservation
|
||||
every {
|
||||
reservationWriter.deleteConfirmed(reservation, memberId)
|
||||
} throws ReservationException(ReservationErrorCode.NOT_RESERVATION_OWNER)
|
||||
|
||||
shouldThrow<ReservationException> {
|
||||
reservationWriteService.deleteReservation(reservationId, memberId)
|
||||
}.also {
|
||||
it.errorCode shouldBe ReservationErrorCode.NOT_RESERVATION_OWNER
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
context("confirmWaiting") {
|
||||
val reservationId = 1L
|
||||
val memberId = 99L // Admin
|
||||
|
||||
test("정상 응답") {
|
||||
every { reservationWriter.confirm(reservationId) } just Runs
|
||||
|
||||
shouldNotThrow<Exception> {
|
||||
reservationWriteService.confirmWaiting(reservationId, memberId)
|
||||
}
|
||||
|
||||
verify(exactly = 1) { reservationWriter.confirm(reservationId) }
|
||||
}
|
||||
|
||||
test("이미 확정된 예약이 있으면 예외 응답") {
|
||||
every {
|
||||
reservationWriter.confirm(reservationId)
|
||||
} throws ReservationException(ReservationErrorCode.CONFIRMED_RESERVATION_ALREADY_EXISTS)
|
||||
|
||||
shouldThrow<ReservationException> {
|
||||
reservationWriteService.confirmWaiting(reservationId, memberId)
|
||||
}.also {
|
||||
it.errorCode shouldBe ReservationErrorCode.CONFIRMED_RESERVATION_ALREADY_EXISTS
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
context("deleteWaiting") {
|
||||
val reservationId = 1L
|
||||
val memberId = 1L
|
||||
val waitingReservation = ReservationFixture.create(
|
||||
id = reservationId,
|
||||
member = MemberFixture.create(id = memberId),
|
||||
status = ReservationStatus.WAITING
|
||||
)
|
||||
|
||||
test("정상 응답") {
|
||||
every { reservationFinder.findById(reservationId) } returns waitingReservation
|
||||
every { reservationWriter.deleteWaiting(waitingReservation, memberId) } just Runs
|
||||
|
||||
shouldNotThrow<Exception> {
|
||||
reservationWriteService.deleteWaiting(reservationId, memberId)
|
||||
}
|
||||
|
||||
verify(exactly = 1) { reservationWriter.deleteWaiting(waitingReservation, memberId) }
|
||||
}
|
||||
|
||||
test("예약을 찾을 수 없으면 예외 응답") {
|
||||
every {
|
||||
reservationFinder.findById(reservationId)
|
||||
} throws ReservationException(ReservationErrorCode.RESERVATION_NOT_FOUND)
|
||||
|
||||
shouldThrow< ReservationException> {
|
||||
reservationWriteService.deleteWaiting(reservationId, memberId)
|
||||
}.also {
|
||||
it.errorCode shouldBe ReservationErrorCode.RESERVATION_NOT_FOUND
|
||||
}
|
||||
}
|
||||
|
||||
test("대기 상태가 아니면 예외 응답") {
|
||||
every { reservationFinder.findById(reservationId) } returns waitingReservation
|
||||
every {
|
||||
reservationWriter.deleteWaiting(waitingReservation, memberId)
|
||||
} throws ReservationException(ReservationErrorCode.ALREADY_CONFIRMED)
|
||||
|
||||
shouldThrow<ReservationException> {
|
||||
reservationWriteService.deleteWaiting(reservationId, memberId)
|
||||
}.also {
|
||||
it.errorCode shouldBe ReservationErrorCode.ALREADY_CONFIRMED
|
||||
}
|
||||
}
|
||||
|
||||
test("삭제하려는 회원이 관리자가 아니고, 대기한 회원과 다르면 예외 응답") {
|
||||
every { reservationFinder.findById(reservationId) } returns waitingReservation
|
||||
every {
|
||||
reservationWriter.deleteWaiting(waitingReservation, memberId)
|
||||
} throws ReservationException(ReservationErrorCode.NOT_RESERVATION_OWNER)
|
||||
|
||||
shouldThrow<ReservationException> {
|
||||
reservationWriteService.deleteWaiting(reservationId, memberId)
|
||||
}.also {
|
||||
it.errorCode shouldBe ReservationErrorCode.NOT_RESERVATION_OWNER
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
@ -1,118 +0,0 @@
|
||||
package roomescape.reservation.business
|
||||
|
||||
import io.kotest.assertions.assertSoftly
|
||||
import io.kotest.assertions.throwables.shouldThrow
|
||||
import io.kotest.core.spec.style.FunSpec
|
||||
import io.kotest.matchers.collections.shouldHaveSize
|
||||
import io.kotest.matchers.shouldBe
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import roomescape.reservation.exception.ReservationErrorCode
|
||||
import roomescape.reservation.exception.ReservationException
|
||||
import roomescape.reservation.implement.ReservationFinder
|
||||
import roomescape.reservation.infrastructure.persistence.ReservationStatus
|
||||
import roomescape.reservation.web.MyReservationRetrieveResponse
|
||||
import roomescape.util.MemberFixture
|
||||
import roomescape.util.ReservationFixture
|
||||
import roomescape.util.ThemeFixture
|
||||
import java.time.LocalDate
|
||||
|
||||
class ReservationQueryServiceTest : FunSpec({
|
||||
|
||||
val reservationFinder: ReservationFinder = mockk()
|
||||
val reservationFindService = ReservationFindService(reservationFinder)
|
||||
|
||||
context("findReservations") {
|
||||
test("정상 응답") {
|
||||
val confirmedReservations = listOf(
|
||||
ReservationFixture.create(status = ReservationStatus.CONFIRMED),
|
||||
ReservationFixture.create(status = ReservationStatus.CONFIRMED_PAYMENT_REQUIRED)
|
||||
)
|
||||
every {
|
||||
reservationFinder.findAllByStatuses(*ReservationStatus.confirmedStatus())
|
||||
} returns confirmedReservations
|
||||
|
||||
val response = reservationFindService.findReservations()
|
||||
|
||||
assertSoftly(response.reservations) {
|
||||
this shouldHaveSize 2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
context("findAllWaiting") {
|
||||
test("정상 응답") {
|
||||
val waitingReservations = listOf(
|
||||
ReservationFixture.create(status = ReservationStatus.WAITING),
|
||||
ReservationFixture.create(status = ReservationStatus.WAITING)
|
||||
)
|
||||
every {
|
||||
reservationFinder.findAllByStatuses(ReservationStatus.WAITING)
|
||||
} returns waitingReservations
|
||||
|
||||
val response = reservationFindService.findAllWaiting()
|
||||
|
||||
assertSoftly(response.reservations) {
|
||||
this shouldHaveSize 2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
context("findReservationsByMemberId") {
|
||||
val memberId = 1L
|
||||
test("정상 응답") {
|
||||
val myReservations = listOf<MyReservationRetrieveResponse>(mockk(), mockk())
|
||||
|
||||
every {
|
||||
reservationFinder.findAllByMemberId(memberId)
|
||||
} returns myReservations
|
||||
|
||||
val response = reservationFindService.findReservationsByMemberId(memberId)
|
||||
|
||||
response.reservations shouldHaveSize 2
|
||||
}
|
||||
}
|
||||
|
||||
context("searchReservations") {
|
||||
val themeId = 1L
|
||||
val memberId = 1L
|
||||
val startFrom = LocalDate.now()
|
||||
val endAt = LocalDate.now().plusDays(1)
|
||||
|
||||
test("정상 응답") {
|
||||
val searchedReservations = listOf(
|
||||
ReservationFixture.create(
|
||||
theme = ThemeFixture.create(themeId),
|
||||
member = MemberFixture.create(memberId),
|
||||
date = startFrom
|
||||
)
|
||||
)
|
||||
|
||||
every {
|
||||
reservationFinder.searchReservations(themeId, memberId, startFrom, endAt)
|
||||
} returns searchedReservations
|
||||
|
||||
val response = reservationFindService.searchReservations(themeId, memberId, startFrom, endAt)
|
||||
|
||||
assertSoftly(response.reservations) {
|
||||
this shouldHaveSize 1
|
||||
this[0].theme.id shouldBe themeId
|
||||
this[0].member.id shouldBe memberId
|
||||
this[0].date shouldBe startFrom
|
||||
}
|
||||
}
|
||||
|
||||
test("종료 날짜가 시작 날짜 이전이면 예외 응답") {
|
||||
val invalidEndAt = startFrom.minusDays(1)
|
||||
every {
|
||||
reservationFinder.searchReservations(themeId, memberId, startFrom, invalidEndAt)
|
||||
} throws ReservationException(ReservationErrorCode.INVALID_SEARCH_DATE_RANGE)
|
||||
|
||||
shouldThrow<ReservationException> {
|
||||
reservationFindService.searchReservations(themeId, memberId, startFrom, invalidEndAt)
|
||||
}.also {
|
||||
it.errorCode shouldBe ReservationErrorCode.INVALID_SEARCH_DATE_RANGE
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
@ -1,64 +0,0 @@
|
||||
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 io.mockk.spyk
|
||||
import io.mockk.verify
|
||||
import org.springframework.data.jpa.domain.Specification
|
||||
import org.springframework.data.repository.findByIdOrNull
|
||||
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.ReservationSearchSpecification
|
||||
import roomescape.reservation.infrastructure.persistence.ReservationStatus
|
||||
import roomescape.time.exception.TimeErrorCode
|
||||
import roomescape.time.exception.TimeException
|
||||
import java.time.LocalDate
|
||||
|
||||
class ReservationFinderTest : FunSpec({
|
||||
val reservationRepository: ReservationRepository = mockk()
|
||||
val reservationValidator = ReservationValidator(reservationRepository)
|
||||
|
||||
val reservationFinder = ReservationFinder(reservationRepository, reservationValidator)
|
||||
|
||||
context("findById") {
|
||||
val reservationId = 1L
|
||||
test("동일한 ID인 시간을 찾아 응답한다.") {
|
||||
every {
|
||||
reservationRepository.findByIdOrNull(reservationId)
|
||||
} returns mockk()
|
||||
|
||||
reservationFinder.findById(reservationId)
|
||||
|
||||
verify(exactly = 1) {
|
||||
reservationRepository.findByIdOrNull(reservationId)
|
||||
}
|
||||
}
|
||||
|
||||
test("동일한 ID인 시간이 없으면 실패한다.") {
|
||||
every {
|
||||
reservationRepository.findByIdOrNull(reservationId)
|
||||
} returns null
|
||||
|
||||
shouldThrow<ReservationException> {
|
||||
reservationFinder.findById(reservationId)
|
||||
}.also {
|
||||
it.errorCode shouldBe ReservationErrorCode.RESERVATION_NOT_FOUND
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
context("searchReservations") {
|
||||
test("시작 날짜가 종료 날짜 이전이면 실패한다.") {
|
||||
shouldThrow<ReservationException> {
|
||||
reservationFinder.searchReservations(1L, 1L, LocalDate.now(), LocalDate.now().minusDays(1))
|
||||
}.also {
|
||||
it.errorCode shouldBe ReservationErrorCode.INVALID_SEARCH_DATE_RANGE
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
@ -1,170 +0,0 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
@ -1,246 +0,0 @@
|
||||
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<TimeException> {
|
||||
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<ReservationException> {
|
||||
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<ThemeException> {
|
||||
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<MemberException> {
|
||||
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<ReservationException> {
|
||||
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<ReservationException> {
|
||||
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<MemberException> {
|
||||
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<ReservationException> {
|
||||
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<ReservationException> {
|
||||
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<ReservationException> {
|
||||
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<ReservationException> {
|
||||
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<ReservationException> {
|
||||
reservationWriter.confirm(reservationId)
|
||||
}.also {
|
||||
it.errorCode shouldBe ReservationErrorCode.CONFIRMED_RESERVATION_ALREADY_EXISTS
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
@ -1,195 +0,0 @@
|
||||
package roomescape.reservation.infrastructure.persistence
|
||||
|
||||
import io.kotest.assertions.assertSoftly
|
||||
import io.kotest.core.spec.style.StringSpec
|
||||
import io.kotest.matchers.collections.shouldContainExactly
|
||||
import io.kotest.matchers.collections.shouldHaveSize
|
||||
import jakarta.persistence.EntityManager
|
||||
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest
|
||||
import roomescape.member.infrastructure.persistence.MemberEntity
|
||||
import roomescape.theme.infrastructure.persistence.ThemeEntity
|
||||
import roomescape.time.infrastructure.persistence.TimeEntity
|
||||
import roomescape.util.MemberFixture
|
||||
import roomescape.util.ReservationFixture
|
||||
import roomescape.util.ThemeFixture
|
||||
import roomescape.util.TimeFixture
|
||||
import java.time.LocalDate
|
||||
|
||||
@DataJpaTest(showSql = false)
|
||||
class ReservationSearchSpecificationTest(
|
||||
val entityManager: EntityManager,
|
||||
val reservationRepository: ReservationRepository
|
||||
) : StringSpec() {
|
||||
|
||||
init {
|
||||
lateinit var confirmedNow: ReservationEntity
|
||||
lateinit var confirmedNotPaidYesterday: ReservationEntity
|
||||
lateinit var waitingTomorrow: ReservationEntity
|
||||
lateinit var member: MemberEntity
|
||||
lateinit var time: TimeEntity
|
||||
lateinit var theme: ThemeEntity
|
||||
|
||||
"동일한 테마의 예약을 조회한다" {
|
||||
val spec = ReservationSearchSpecification()
|
||||
.sameThemeId(theme.id)
|
||||
.build()
|
||||
|
||||
val results: List<ReservationEntity> = reservationRepository.findAll(spec)
|
||||
|
||||
assertSoftly(results) {
|
||||
this shouldHaveSize 3
|
||||
this shouldContainExactly listOf(confirmedNow, confirmedNotPaidYesterday, waitingTomorrow)
|
||||
}
|
||||
}
|
||||
|
||||
"동일한 회원의 예약을 조회한다" {
|
||||
val spec = ReservationSearchSpecification()
|
||||
.sameMemberId(member.id)
|
||||
.build()
|
||||
|
||||
val results: List<ReservationEntity> = reservationRepository.findAll(spec)
|
||||
|
||||
assertSoftly(results) {
|
||||
this shouldHaveSize 3
|
||||
this shouldContainExactly listOf(confirmedNow, confirmedNotPaidYesterday, waitingTomorrow)
|
||||
}
|
||||
}
|
||||
|
||||
"동일한 예약 시간의 예약을 조회한다" {
|
||||
val spec = ReservationSearchSpecification()
|
||||
.sameTimeId(time.id)
|
||||
.build()
|
||||
|
||||
val results: List<ReservationEntity> = reservationRepository.findAll(spec)
|
||||
|
||||
assertSoftly(results) {
|
||||
this shouldHaveSize 3
|
||||
this shouldContainExactly listOf(confirmedNow, confirmedNotPaidYesterday, waitingTomorrow)
|
||||
}
|
||||
}
|
||||
|
||||
"동일한 날짜의 예약을 조회한다" {
|
||||
val spec = ReservationSearchSpecification()
|
||||
.sameDate(LocalDate.now())
|
||||
.build()
|
||||
|
||||
val results: List<ReservationEntity> = reservationRepository.findAll(spec)
|
||||
|
||||
assertSoftly(results) {
|
||||
this shouldHaveSize 1
|
||||
this shouldContainExactly listOf(confirmedNow)
|
||||
}
|
||||
}
|
||||
|
||||
"여러 상태를 입력받아 같은 상태안 예약을 조회한다." {
|
||||
val spec = ReservationSearchSpecification()
|
||||
.status(
|
||||
ReservationStatus.CONFIRMED,
|
||||
ReservationStatus.CONFIRMED_PAYMENT_REQUIRED,
|
||||
ReservationStatus.WAITING
|
||||
).build()
|
||||
|
||||
val results: List<ReservationEntity> = reservationRepository.findAll(spec)
|
||||
|
||||
assertSoftly(results) {
|
||||
this shouldHaveSize reservationRepository.findAll().size
|
||||
}
|
||||
}
|
||||
|
||||
"확정 상태인 예약을 조회한다" {
|
||||
val spec = ReservationSearchSpecification()
|
||||
.confirmed()
|
||||
.build()
|
||||
|
||||
val results: List<ReservationEntity> = reservationRepository.findAll(spec)
|
||||
|
||||
assertSoftly(results) {
|
||||
this shouldHaveSize 2
|
||||
this shouldContainExactly listOf(confirmedNow, confirmedNotPaidYesterday)
|
||||
}
|
||||
}
|
||||
|
||||
"대기 상태인 예약을 조회한다" {
|
||||
val spec = ReservationSearchSpecification()
|
||||
.waiting()
|
||||
.build()
|
||||
|
||||
val results: List<ReservationEntity> = reservationRepository.findAll(spec)
|
||||
|
||||
assertSoftly(results) {
|
||||
this shouldHaveSize 1
|
||||
this shouldContainExactly listOf(waitingTomorrow)
|
||||
}
|
||||
}
|
||||
|
||||
"예약 날짜가 오늘 이후인 예약을 조회한다" {
|
||||
val spec = ReservationSearchSpecification()
|
||||
.dateStartFrom(LocalDate.now())
|
||||
.build()
|
||||
|
||||
val results: List<ReservationEntity> = reservationRepository.findAll(spec)
|
||||
|
||||
assertSoftly(results) {
|
||||
this shouldHaveSize 2
|
||||
this shouldContainExactly listOf(confirmedNow, waitingTomorrow)
|
||||
}
|
||||
}
|
||||
|
||||
"예약 날짜가 내일 이전인 예약을 조회한다" {
|
||||
val spec = ReservationSearchSpecification()
|
||||
.dateEndAt(LocalDate.now().plusDays(1))
|
||||
.build()
|
||||
|
||||
val results: List<ReservationEntity> = reservationRepository.findAll(spec)
|
||||
|
||||
assertSoftly(results) {
|
||||
this shouldHaveSize 3
|
||||
this shouldContainExactly listOf(confirmedNow, confirmedNotPaidYesterday, waitingTomorrow)
|
||||
}
|
||||
}
|
||||
|
||||
beforeTest {
|
||||
member = MemberFixture.create().also {
|
||||
entityManager.persist(it)
|
||||
}
|
||||
time = TimeFixture.create().also {
|
||||
entityManager.persist(it)
|
||||
}
|
||||
theme = ThemeFixture.create().also {
|
||||
entityManager.persist(it)
|
||||
}
|
||||
|
||||
confirmedNow = ReservationFixture.create(
|
||||
time = time,
|
||||
member = member,
|
||||
theme = theme,
|
||||
date = LocalDate.now(),
|
||||
status = ReservationStatus.CONFIRMED
|
||||
).also {
|
||||
entityManager.persist(it)
|
||||
}
|
||||
|
||||
confirmedNotPaidYesterday = ReservationFixture.create(
|
||||
time = time,
|
||||
member = member,
|
||||
theme = theme,
|
||||
date = LocalDate.now().minusDays(1),
|
||||
status = ReservationStatus.CONFIRMED_PAYMENT_REQUIRED
|
||||
).also {
|
||||
entityManager.persist(it)
|
||||
}
|
||||
|
||||
waitingTomorrow = ReservationFixture.create(
|
||||
time = time,
|
||||
member = member,
|
||||
theme = theme,
|
||||
date = LocalDate.now().plusDays(1),
|
||||
status = ReservationStatus.WAITING
|
||||
).also {
|
||||
entityManager.persist(it)
|
||||
}
|
||||
|
||||
entityManager.flush()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,162 +0,0 @@
|
||||
package roomescape.theme.business
|
||||
|
||||
import io.kotest.assertions.assertSoftly
|
||||
import io.kotest.assertions.throwables.shouldNotThrow
|
||||
import io.kotest.assertions.throwables.shouldThrow
|
||||
import io.kotest.core.spec.style.FunSpec
|
||||
import io.kotest.matchers.collections.shouldContainExactly
|
||||
import io.kotest.matchers.shouldBe
|
||||
import io.mockk.*
|
||||
import roomescape.theme.exception.ThemeErrorCode
|
||||
import roomescape.theme.exception.ThemeException
|
||||
import roomescape.theme.implement.ThemeFinder
|
||||
import roomescape.theme.implement.ThemeWriter
|
||||
import roomescape.theme.infrastructure.persistence.ThemeEntity
|
||||
import roomescape.theme.web.ThemeCreateRequest
|
||||
import roomescape.theme.web.ThemeCreateResponse
|
||||
import roomescape.util.ThemeFixture
|
||||
import java.time.LocalDate
|
||||
|
||||
class ThemeServiceTest : FunSpec({
|
||||
val themeFinder: ThemeFinder = mockk()
|
||||
val themeWriter: ThemeWriter = mockk()
|
||||
|
||||
val themeService = ThemeService(themeFinder, themeWriter)
|
||||
|
||||
context("findThemeById") {
|
||||
val themeId = 1L
|
||||
|
||||
test("정상 응답") {
|
||||
val theme: ThemeEntity = ThemeFixture.create(id = themeId)
|
||||
every {
|
||||
themeFinder.findById(themeId)
|
||||
} returns theme
|
||||
|
||||
theme.id shouldBe themeId
|
||||
}
|
||||
|
||||
test("테마를 찾을 수 없으면 예외 응답") {
|
||||
every {
|
||||
themeFinder.findById(themeId)
|
||||
} throws ThemeException(ThemeErrorCode.THEME_NOT_FOUND)
|
||||
|
||||
shouldThrow<ThemeException> {
|
||||
themeService.findById(themeId)
|
||||
}.also {
|
||||
it.errorCode shouldBe ThemeErrorCode.THEME_NOT_FOUND
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
context("findThemes") {
|
||||
test("정상 응답") {
|
||||
val themes = listOf(
|
||||
ThemeFixture.create(id = 1, name = "t1"),
|
||||
ThemeFixture.create(id = 2, name = "t2")
|
||||
)
|
||||
|
||||
every {
|
||||
themeFinder.findAll()
|
||||
} returns themes
|
||||
|
||||
val response = themeService.findThemes()
|
||||
|
||||
assertSoftly(response.themes) {
|
||||
this.size shouldBe themes.size
|
||||
it.map { theme -> theme.name } shouldContainExactly themes.map { theme -> theme.name }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
context("findMostReservedThemes") {
|
||||
test("7일 전 부터 1일 전 까지 조회") {
|
||||
val count = 10
|
||||
val startFrom = slot<LocalDate>()
|
||||
val endAt = slot<LocalDate>()
|
||||
|
||||
every {
|
||||
themeFinder.findMostReservedThemes(count, capture(startFrom), capture(endAt))
|
||||
} returns emptyList()
|
||||
|
||||
themeService.findMostReservedThemes(count)
|
||||
|
||||
startFrom.captured shouldBe LocalDate.now().minusDays(7)
|
||||
endAt.captured shouldBe LocalDate.now().minusDays(1)
|
||||
}
|
||||
}
|
||||
|
||||
context("createTheme") {
|
||||
val request = ThemeCreateRequest(
|
||||
name = "New Theme",
|
||||
description = "Description",
|
||||
thumbnail = "http://example.com/thumbnail.jpg"
|
||||
)
|
||||
|
||||
test("정상 저장") {
|
||||
every {
|
||||
themeWriter.create(request.name, request.description, request.thumbnail)
|
||||
} returns ThemeFixture.create(
|
||||
id = 1,
|
||||
name = request.name,
|
||||
description = request.description,
|
||||
thumbnail = request.thumbnail
|
||||
)
|
||||
|
||||
val response: ThemeCreateResponse = themeService.createTheme(request)
|
||||
|
||||
assertSoftly(response) {
|
||||
this.id shouldBe 1L
|
||||
this.name shouldBe request.name
|
||||
this.description shouldBe request.description
|
||||
this.thumbnail shouldBe request.thumbnail
|
||||
}
|
||||
}
|
||||
|
||||
test("중복된 이름이 있으면 예외 응답") {
|
||||
every {
|
||||
themeWriter.create(request.name, request.description, request.thumbnail)
|
||||
} throws ThemeException(ThemeErrorCode.THEME_NAME_DUPLICATED)
|
||||
|
||||
shouldThrow<ThemeException> {
|
||||
themeService.createTheme(request)
|
||||
}.also {
|
||||
it.errorCode shouldBe ThemeErrorCode.THEME_NAME_DUPLICATED
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
context("deleteById") {
|
||||
val themeId = 1L
|
||||
val theme: ThemeEntity = ThemeFixture.create(id = themeId)
|
||||
|
||||
test("정상 응답") {
|
||||
every { themeFinder.findById(themeId) } returns theme
|
||||
every { themeWriter.delete(theme) } just Runs
|
||||
|
||||
shouldNotThrow<ThemeException> {
|
||||
themeService.deleteTheme(themeId)
|
||||
}
|
||||
}
|
||||
|
||||
test("테마를 찾을 수 없으면 예외 응답") {
|
||||
every { themeFinder.findById(themeId) } throws ThemeException(ThemeErrorCode.THEME_NOT_FOUND)
|
||||
|
||||
shouldThrow<ThemeException> {
|
||||
themeService.deleteTheme(themeId)
|
||||
}.also {
|
||||
it.errorCode shouldBe ThemeErrorCode.THEME_NOT_FOUND
|
||||
}
|
||||
}
|
||||
|
||||
test("예약이 있는 테마이면 예외 응답") {
|
||||
every { themeFinder.findById(themeId) } returns theme
|
||||
every { themeWriter.delete(theme) } throws ThemeException(ThemeErrorCode.THEME_ALREADY_RESERVED)
|
||||
|
||||
shouldThrow<ThemeException> {
|
||||
themeService.deleteTheme(themeId)
|
||||
}.also {
|
||||
it.errorCode shouldBe ThemeErrorCode.THEME_ALREADY_RESERVED
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
@ -1,76 +0,0 @@
|
||||
package roomescape.theme.implement
|
||||
|
||||
import io.kotest.assertions.throwables.shouldThrow
|
||||
import io.kotest.core.spec.style.FunSpec
|
||||
import io.kotest.matchers.collections.shouldHaveSize
|
||||
import io.kotest.matchers.shouldBe
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.verify
|
||||
import org.springframework.data.repository.findByIdOrNull
|
||||
import roomescape.theme.exception.ThemeErrorCode
|
||||
import roomescape.theme.exception.ThemeException
|
||||
import roomescape.theme.infrastructure.persistence.ThemeRepository
|
||||
import roomescape.util.ThemeFixture
|
||||
import java.time.LocalDate
|
||||
|
||||
class ThemeFinderTest : FunSpec({
|
||||
val themeRepository: ThemeRepository = mockk()
|
||||
|
||||
val themeFinder = ThemeFinder(themeRepository)
|
||||
|
||||
context("findAll") {
|
||||
test("모든 테마를 조회한다.") {
|
||||
every {
|
||||
themeRepository.findAll()
|
||||
} returns listOf(mockk(), mockk(), mockk())
|
||||
|
||||
themeRepository.findAll() shouldHaveSize 3
|
||||
}
|
||||
}
|
||||
|
||||
context("findById") {
|
||||
val timeId = 1L
|
||||
test("동일한 ID인 테마를 찾아 응답한다.") {
|
||||
every {
|
||||
themeRepository.findByIdOrNull(timeId)
|
||||
} returns mockk()
|
||||
|
||||
themeFinder.findById(timeId)
|
||||
|
||||
verify(exactly = 1) {
|
||||
themeRepository.findByIdOrNull(timeId)
|
||||
}
|
||||
}
|
||||
|
||||
test("동일한 ID인 테마가 없으면 실패한다.") {
|
||||
every {
|
||||
themeRepository.findByIdOrNull(timeId)
|
||||
} returns null
|
||||
|
||||
shouldThrow<ThemeException> {
|
||||
themeFinder.findById(timeId)
|
||||
}.also {
|
||||
it.errorCode shouldBe ThemeErrorCode.THEME_NOT_FOUND
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
context("findMostReservedThemes") {
|
||||
test("입력된 개수보다 조회된 개수가 작으면 조회된 개수만큼 반환한다.") {
|
||||
val count = 10
|
||||
val startFrom = LocalDate.now().minusDays(7)
|
||||
val endAt = LocalDate.now().minusDays(1)
|
||||
|
||||
every {
|
||||
themeRepository.findPopularThemes(startFrom, endAt, count)
|
||||
} returns listOf(
|
||||
ThemeFixture.create(id = 1, name = "name1"),
|
||||
ThemeFixture.create(id = 2, name = "name2"),
|
||||
ThemeFixture.create(id = 3, name = "name3"),
|
||||
)
|
||||
|
||||
themeFinder.findMostReservedThemes(count, startFrom, endAt) shouldHaveSize 3
|
||||
}
|
||||
}
|
||||
})
|
||||
@ -1,79 +0,0 @@
|
||||
package roomescape.theme.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 roomescape.theme.exception.ThemeErrorCode
|
||||
import roomescape.theme.exception.ThemeException
|
||||
import roomescape.theme.infrastructure.persistence.ThemeEntity
|
||||
import roomescape.theme.infrastructure.persistence.ThemeRepository
|
||||
import roomescape.util.ThemeFixture
|
||||
|
||||
class ThemeValidatorTest : FunSpec({
|
||||
val themeRepository: ThemeRepository = mockk()
|
||||
|
||||
val themeValidator = ThemeValidator(themeRepository)
|
||||
|
||||
context("validateNameAlreadyExists") {
|
||||
val name = "name"
|
||||
|
||||
test("같은 이름을 가진 테마가 있으면 예외를 던진다.") {
|
||||
every {
|
||||
themeRepository.existsByName(name)
|
||||
} returns true
|
||||
|
||||
shouldThrow<ThemeException> {
|
||||
themeValidator.validateNameAlreadyExists(name)
|
||||
}.also {
|
||||
it.errorCode shouldBe ThemeErrorCode.THEME_NAME_DUPLICATED
|
||||
}
|
||||
}
|
||||
|
||||
test("같은 이름을 가진 테마가 없으면 종료한다.") {
|
||||
every {
|
||||
themeRepository.existsByName(name)
|
||||
} returns false
|
||||
|
||||
shouldNotThrow<ThemeException> {
|
||||
themeValidator.validateNameAlreadyExists(name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
context("validateIsReserved") {
|
||||
test("입력된 id가 null 이면 예외를 던진다.") {
|
||||
shouldThrow<ThemeException> {
|
||||
themeValidator.validateIsReserved(ThemeFixture.create(id = null))
|
||||
}.also {
|
||||
it.errorCode shouldBe ThemeErrorCode.INVALID_REQUEST_VALUE
|
||||
}
|
||||
}
|
||||
|
||||
val theme: ThemeEntity = ThemeFixture.create(id = 1L, name = "name")
|
||||
|
||||
test("예약이 있는 테마이면 예외를 던진다.") {
|
||||
every {
|
||||
themeRepository.isReservedTheme(theme.id!!)
|
||||
} returns true
|
||||
|
||||
shouldThrow<ThemeException> {
|
||||
themeValidator.validateIsReserved(theme)
|
||||
}.also {
|
||||
it.errorCode shouldBe ThemeErrorCode.THEME_ALREADY_RESERVED
|
||||
}
|
||||
}
|
||||
|
||||
test("예약이 없는 테마이면 종료한다.") {
|
||||
every {
|
||||
themeRepository.isReservedTheme(theme.id!!)
|
||||
} returns false
|
||||
|
||||
shouldNotThrow<ThemeException> {
|
||||
themeValidator.validateIsReserved(theme)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
@ -1,86 +0,0 @@
|
||||
package roomescape.theme.implement
|
||||
|
||||
import io.kotest.assertions.throwables.shouldThrow
|
||||
import io.kotest.core.spec.style.FunSpec
|
||||
import io.kotest.matchers.shouldBe
|
||||
import io.mockk.*
|
||||
import roomescape.common.config.next
|
||||
import roomescape.theme.exception.ThemeErrorCode
|
||||
import roomescape.theme.exception.ThemeException
|
||||
import roomescape.theme.infrastructure.persistence.ThemeEntity
|
||||
import roomescape.theme.infrastructure.persistence.ThemeRepository
|
||||
import roomescape.util.ThemeFixture
|
||||
import roomescape.util.TsidFactory
|
||||
|
||||
class ThemeWriterTest : FunSpec({
|
||||
|
||||
val themeValidator: ThemeValidator = mockk()
|
||||
val themeRepository: ThemeRepository = mockk()
|
||||
|
||||
val themeWriter = ThemeWriter(themeValidator, themeRepository, TsidFactory)
|
||||
|
||||
context("create") {
|
||||
val name = "name"
|
||||
val description = "description"
|
||||
val thumbnail = "thumbnail"
|
||||
|
||||
test("중복된 이름이 있으면 실패한다.") {
|
||||
every {
|
||||
themeValidator.validateNameAlreadyExists(name)
|
||||
} throws ThemeException(ThemeErrorCode.THEME_NAME_DUPLICATED)
|
||||
|
||||
shouldThrow<ThemeException> {
|
||||
themeWriter.create(name, description, thumbnail)
|
||||
}.also {
|
||||
it.errorCode shouldBe ThemeErrorCode.THEME_NAME_DUPLICATED
|
||||
}
|
||||
}
|
||||
|
||||
test("중복된 이름이 없으면 저장한다.") {
|
||||
every {
|
||||
themeValidator.validateNameAlreadyExists(name)
|
||||
} just Runs
|
||||
|
||||
every {
|
||||
themeRepository.save(any())
|
||||
} returns ThemeFixture.create(name = name, description = description, thumbnail = thumbnail)
|
||||
|
||||
themeWriter.create(name, description, thumbnail)
|
||||
|
||||
verify(exactly = 1) {
|
||||
themeRepository.save(any())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
context("delete") {
|
||||
val theme: ThemeEntity = ThemeFixture.create(id = TsidFactory.next())
|
||||
test("예약이 있는 테마이면 실패한다.") {
|
||||
every {
|
||||
themeValidator.validateIsReserved(theme)
|
||||
} throws ThemeException(ThemeErrorCode.THEME_ALREADY_RESERVED)
|
||||
|
||||
shouldThrow<ThemeException> {
|
||||
themeWriter.delete(theme)
|
||||
}.also {
|
||||
it.errorCode shouldBe ThemeErrorCode.THEME_ALREADY_RESERVED
|
||||
}
|
||||
}
|
||||
|
||||
test("예약이 없는 테마이면 제거한다.") {
|
||||
every {
|
||||
themeValidator.validateIsReserved(theme)
|
||||
} just Runs
|
||||
|
||||
every {
|
||||
themeRepository.delete(theme)
|
||||
} just Runs
|
||||
|
||||
themeWriter.delete(theme)
|
||||
|
||||
verify(exactly = 1) {
|
||||
themeRepository.delete(theme)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
@ -1,144 +0,0 @@
|
||||
package roomescape.theme.infrastructure.persistence
|
||||
|
||||
import io.kotest.core.spec.style.FunSpec
|
||||
import io.kotest.matchers.collections.shouldContainInOrder
|
||||
import io.kotest.matchers.shouldBe
|
||||
import jakarta.persistence.EntityManager
|
||||
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest
|
||||
import roomescape.theme.util.TestThemeDataHelper
|
||||
import java.time.LocalDate
|
||||
|
||||
@DataJpaTest(showSql = false)
|
||||
class ThemeRepositoryTest(
|
||||
val themeRepository: ThemeRepository,
|
||||
val entityManager: EntityManager
|
||||
) : FunSpec() {
|
||||
|
||||
val helper = TestThemeDataHelper(entityManager, transactionTemplate = null)
|
||||
|
||||
init {
|
||||
context("findTopNThemeBetweenStartDateAndEndDate") {
|
||||
beforeTest {
|
||||
for (i in 1..10) {
|
||||
helper.createThemeWithReservations(
|
||||
name = "테마$i",
|
||||
reservedCount = i,
|
||||
date = LocalDate.now().minusDays(i.toLong()),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
test("지난 10일간 예약 수가 가장 많은 테마 5개를 조회") {
|
||||
themeRepository.findPopularThemes(
|
||||
LocalDate.now().minusDays(10),
|
||||
LocalDate.now().minusDays(1),
|
||||
5
|
||||
).also { themes ->
|
||||
themes.size shouldBe 5
|
||||
themes.map { it.name } shouldContainInOrder listOf(
|
||||
"테마10", "테마9", "테마8", "테마7", "테마6"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
test("8일 전부터 5일 전까지 예약 수가 가장 많은 테마 3개를 조회") {
|
||||
themeRepository.findPopularThemes(
|
||||
LocalDate.now().minusDays(8),
|
||||
LocalDate.now().minusDays(5),
|
||||
3
|
||||
).also { themes ->
|
||||
themes.size shouldBe 3
|
||||
themes.map { it.name } shouldContainInOrder listOf(
|
||||
"테마8", "테마7", "테마6"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
test("예약 수가 동일하면 먼저 생성된 테마를 우선 조회") {
|
||||
helper.createThemeWithReservations(
|
||||
name = "테마11",
|
||||
reservedCount = 5,
|
||||
date = LocalDate.now().minusDays(5),
|
||||
)
|
||||
|
||||
themeRepository.findPopularThemes(
|
||||
LocalDate.now().minusDays(6),
|
||||
LocalDate.now().minusDays(4),
|
||||
5
|
||||
).also { themes ->
|
||||
themes.size shouldBe 4
|
||||
themes.map { it.name } shouldContainInOrder listOf(
|
||||
"테마6", "테마5", "테마11", "테마4"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
test("입력된 갯수보다 조회된 갯수가 작으면, 조회된 갯수만큼 반환") {
|
||||
themeRepository.findPopularThemes(
|
||||
LocalDate.now().minusDays(10),
|
||||
LocalDate.now().minusDays(6),
|
||||
10
|
||||
).also { themes ->
|
||||
themes.size shouldBe 5
|
||||
}
|
||||
}
|
||||
|
||||
test("입력된 갯수보다 조회된 갯수가 많으면, 입력된 갯수만큼 반환") {
|
||||
themeRepository.findPopularThemes(
|
||||
LocalDate.now().minusDays(10),
|
||||
LocalDate.now().minusDays(1),
|
||||
15
|
||||
).also { themes ->
|
||||
themes.size shouldBe 10
|
||||
}
|
||||
}
|
||||
|
||||
test("입력된 날짜 범위에 예약된 테마가 없을 경우 빈 리스트 반환") {
|
||||
themeRepository.findPopularThemes(
|
||||
LocalDate.now().plusDays(1),
|
||||
LocalDate.now().plusDays(10),
|
||||
5
|
||||
).also { themes ->
|
||||
themes.size shouldBe 0
|
||||
}
|
||||
}
|
||||
}
|
||||
context("existsByName ") {
|
||||
val themeName = "test-theme"
|
||||
beforeTest {
|
||||
helper.createThemeWithReservations(
|
||||
name = themeName,
|
||||
reservedCount = 0,
|
||||
date = LocalDate.now()
|
||||
)
|
||||
}
|
||||
test("테마 이름이 존재하면 true 반환") {
|
||||
themeRepository.existsByName(themeName) shouldBe true
|
||||
}
|
||||
|
||||
test("테마 이름이 존재하지 않으면 false 반환") {
|
||||
themeRepository.existsByName(themeName.repeat(2)) shouldBe false
|
||||
}
|
||||
}
|
||||
|
||||
context("isReservedTheme") {
|
||||
test("테마가 예약 중이면 true 반환") {
|
||||
val theme = helper.createThemeWithReservations(
|
||||
name = "예약된 테마",
|
||||
reservedCount = 1,
|
||||
date = LocalDate.now()
|
||||
)
|
||||
themeRepository.isReservedTheme(theme.id!!) shouldBe true
|
||||
}
|
||||
|
||||
test("테마가 예약 중이 아니면 false 반환") {
|
||||
val theme = helper.createThemeWithReservations(
|
||||
name = "예약되지 않은 테마",
|
||||
reservedCount = 0,
|
||||
date = LocalDate.now()
|
||||
)
|
||||
themeRepository.isReservedTheme(theme.id!!) shouldBe false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,57 +0,0 @@
|
||||
package roomescape.theme.util
|
||||
|
||||
import jakarta.persistence.EntityManager
|
||||
import org.springframework.transaction.support.TransactionTemplate
|
||||
import roomescape.member.infrastructure.persistence.MemberEntity
|
||||
import roomescape.reservation.infrastructure.persistence.ReservationStatus
|
||||
import roomescape.theme.infrastructure.persistence.ThemeEntity
|
||||
import roomescape.time.infrastructure.persistence.TimeEntity
|
||||
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 TestThemeDataHelper(
|
||||
val entityManager: EntityManager,
|
||||
val transactionTemplate: TransactionTemplate?
|
||||
) {
|
||||
/**
|
||||
* GET /themes/most-reserved-last-week API와 관련 Repository 테스트에 사용
|
||||
* @param name: 테마 이름
|
||||
* @param reservedCount: 이 테마가 예약된 횟수
|
||||
* @param date: reservedCount 개의 예약을 만들 때 사용할 날짜
|
||||
*/
|
||||
fun createThemeWithReservations(name: String, reservedCount: Int, date: LocalDate): ThemeEntity =
|
||||
if (transactionTemplate == null) {
|
||||
createAndGet(name, reservedCount, date)
|
||||
} else {
|
||||
transactionTemplate.execute { createAndGet(name, reservedCount, date) }!!
|
||||
}
|
||||
|
||||
|
||||
fun createAndGet(name: String, reservedCount: Int, date: LocalDate): ThemeEntity {
|
||||
val themeEntity: ThemeEntity = ThemeFixture.create(name = name).also { entityManager.persist(it) }
|
||||
val member: MemberEntity = MemberFixture.create().also { entityManager.persist(it) }
|
||||
|
||||
for (i in 1..reservedCount) {
|
||||
val time: TimeEntity = TimeFixture.create(
|
||||
startAt = LocalTime.now().plusMinutes(i.toLong())
|
||||
).also { entityManager.persist(it) }
|
||||
|
||||
ReservationFixture.create(
|
||||
date = date,
|
||||
theme = themeEntity,
|
||||
member = member,
|
||||
time = time,
|
||||
status = ReservationStatus.entries.random()
|
||||
).also { entityManager.persist(it) }
|
||||
}
|
||||
|
||||
entityManager.flush()
|
||||
entityManager.clear()
|
||||
|
||||
return themeEntity
|
||||
}
|
||||
}
|
||||
@ -1,110 +0,0 @@
|
||||
package roomescape.theme.web
|
||||
|
||||
import io.kotest.core.spec.style.FunSpec
|
||||
import io.restassured.module.kotlin.extensions.Given
|
||||
import io.restassured.module.kotlin.extensions.Then
|
||||
import io.restassured.module.kotlin.extensions.When
|
||||
import jakarta.persistence.EntityManager
|
||||
import org.hamcrest.Matchers.equalTo
|
||||
import org.springframework.boot.test.context.SpringBootTest
|
||||
import org.springframework.boot.test.web.server.LocalServerPort
|
||||
import org.springframework.transaction.support.TransactionTemplate
|
||||
import roomescape.theme.util.TestThemeDataHelper
|
||||
import roomescape.util.CleanerMode
|
||||
import roomescape.util.DatabaseCleanerExtension
|
||||
import java.time.LocalDate
|
||||
import kotlin.random.Random
|
||||
|
||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
||||
class MostReservedThemeApiTest(
|
||||
@LocalServerPort val port: Int,
|
||||
val transactionTemplate: TransactionTemplate,
|
||||
val entityManager: EntityManager,
|
||||
) : FunSpec({ extension(DatabaseCleanerExtension(mode = CleanerMode.AFTER_SPEC)) }) {
|
||||
|
||||
val helper = TestThemeDataHelper(entityManager, transactionTemplate)
|
||||
|
||||
init {
|
||||
beforeSpec {
|
||||
transactionTemplate.executeWithoutResult {
|
||||
// 7일 전 ~ 1일 전 예약된 테마 10개 생성
|
||||
for (i in 1..10) {
|
||||
helper.createThemeWithReservations(
|
||||
name = "테마$i",
|
||||
reservedCount = 1,
|
||||
date = LocalDate.now().minusDays(Random.nextLong(1, 7))
|
||||
)
|
||||
}
|
||||
|
||||
// 8일 전 예약된 테마 1개 생성
|
||||
helper.createThemeWithReservations(
|
||||
name = "테마11",
|
||||
reservedCount = 1,
|
||||
date = LocalDate.now().minusDays(8)
|
||||
)
|
||||
|
||||
// 당일 예약된 테마 1개 생성
|
||||
helper.createThemeWithReservations(
|
||||
name = "테마12",
|
||||
reservedCount = 1,
|
||||
date = LocalDate.now()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
context("GET /themes/most-reserved-last-week") {
|
||||
val endpoint = "/themes/most-reserved-last-week"
|
||||
|
||||
test("count 파라미터가 없으면 10개를 반환한다") {
|
||||
Given {
|
||||
port(port)
|
||||
} When {
|
||||
get(endpoint)
|
||||
} Then {
|
||||
statusCode(200)
|
||||
body("data.themes.size()", equalTo(10))
|
||||
}
|
||||
}
|
||||
|
||||
test("조회된 테마가 count보다 적으면 조회된 만큼 반환한다") {
|
||||
val count = 15
|
||||
Given {
|
||||
port(port)
|
||||
param("count", count)
|
||||
} When {
|
||||
get(endpoint)
|
||||
} Then {
|
||||
statusCode(200)
|
||||
body("data.themes.size()", equalTo(10))
|
||||
}
|
||||
}
|
||||
|
||||
test("조회된 테마가 count보다 많으면 count만큼 반환한다") {
|
||||
val count = 5
|
||||
Given {
|
||||
port(port)
|
||||
param("count", count)
|
||||
} When {
|
||||
get(endpoint)
|
||||
} Then {
|
||||
statusCode(200)
|
||||
body("data.themes.size()", equalTo(count))
|
||||
}
|
||||
}
|
||||
|
||||
test("지난 7일 동안의 예약만 집계한다") {
|
||||
// beforeSpec 에서 정의한 테스트 데이터 중, 8일 전 / 당일 예약 테마는 제외되어야 한다.
|
||||
val count = 12
|
||||
Given {
|
||||
port(port)
|
||||
param("count", count)
|
||||
} When {
|
||||
get(endpoint)
|
||||
} Then {
|
||||
statusCode(200)
|
||||
body("data.themes.size()", equalTo(10))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,278 +0,0 @@
|
||||
package roomescape.theme.web
|
||||
|
||||
import com.ninjasquad.springmockk.MockkBean
|
||||
import io.mockk.every
|
||||
import org.hamcrest.Matchers.equalTo
|
||||
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest
|
||||
import org.springframework.http.MediaType
|
||||
import org.springframework.test.web.servlet.MockMvc
|
||||
import roomescape.auth.exception.AuthErrorCode
|
||||
import roomescape.common.exception.CommonErrorCode
|
||||
import roomescape.theme.business.ThemeService
|
||||
import roomescape.theme.exception.ThemeErrorCode
|
||||
import roomescape.theme.exception.ThemeException
|
||||
import roomescape.util.RoomescapeApiTest
|
||||
import roomescape.util.ThemeFixture
|
||||
|
||||
@WebMvcTest(ThemeController::class)
|
||||
class ThemeControllerTest(val mockMvc: MockMvc) : RoomescapeApiTest() {
|
||||
@MockkBean
|
||||
private lateinit var themeService: ThemeService
|
||||
|
||||
init {
|
||||
Given("GET /themes 요청을") {
|
||||
val endpoint = "/themes"
|
||||
|
||||
When("로그인 하지 않은 사용자가 보내면") {
|
||||
doNotLogin()
|
||||
|
||||
Then("예외 응답") {
|
||||
val expectedError = AuthErrorCode.MEMBER_NOT_FOUND
|
||||
runGetTest(
|
||||
mockMvc = mockMvc,
|
||||
endpoint = endpoint,
|
||||
) {
|
||||
status { isEqualTo(expectedError.httpStatus.value()) }
|
||||
}.andExpect {
|
||||
jsonPath("$.code") { value(expectedError.errorCode) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
When("로그인 상태라면") {
|
||||
loginAsUser()
|
||||
|
||||
Then("정상 응답") {
|
||||
every {
|
||||
themeService.findThemes()
|
||||
} returns listOf(
|
||||
ThemeFixture.create(id = 1, name = "theme1"),
|
||||
ThemeFixture.create(id = 2, name = "theme2"),
|
||||
ThemeFixture.create(id = 3, name = "theme3")
|
||||
).toRetrieveListResponse()
|
||||
|
||||
runGetTest(
|
||||
mockMvc = mockMvc,
|
||||
endpoint = endpoint,
|
||||
) {
|
||||
status { isOk() }
|
||||
content {
|
||||
contentType(MediaType.APPLICATION_JSON)
|
||||
jsonPath("$.data.themes[0].id") { value(1) }
|
||||
jsonPath("$.data.themes[1].id") { value(2) }
|
||||
jsonPath("$.data.themes[2].id") { value(3) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Given("POST /themes 요청을") {
|
||||
val endpoint = "/themes"
|
||||
val request = ThemeCreateRequest(
|
||||
name = "theme1",
|
||||
description = "description1",
|
||||
thumbnail = "http://example.com/thumbnail1.jpg"
|
||||
)
|
||||
|
||||
When("로그인 하지 않은 사용자가 보내면") {
|
||||
doNotLogin()
|
||||
|
||||
Then("예외 응답") {
|
||||
val expectedError = AuthErrorCode.MEMBER_NOT_FOUND
|
||||
runPostTest(
|
||||
mockMvc = mockMvc,
|
||||
endpoint = endpoint,
|
||||
body = request,
|
||||
) {
|
||||
status { isEqualTo(expectedError.httpStatus.value()) }
|
||||
jsonPath("$.code", equalTo(expectedError.errorCode))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
When("관리자가 아닌 사용자가 보내면") {
|
||||
loginAsUser()
|
||||
|
||||
Then("예외 응답") {
|
||||
val expectedError = AuthErrorCode.ACCESS_DENIED
|
||||
runPostTest(
|
||||
mockMvc = mockMvc,
|
||||
endpoint = endpoint,
|
||||
body = request,
|
||||
) {
|
||||
status { isEqualTo(expectedError.httpStatus.value()) }
|
||||
jsonPath("$.code") { value(expectedError.errorCode) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
When("관리자가 보낼 때") {
|
||||
beforeTest {
|
||||
loginAsAdmin()
|
||||
}
|
||||
|
||||
Then("동일한 이름의 테마가 있으면 예외 응답") {
|
||||
val expectedError = ThemeErrorCode.THEME_NAME_DUPLICATED
|
||||
|
||||
every {
|
||||
themeService.createTheme(request)
|
||||
} throws ThemeException(expectedError)
|
||||
|
||||
runPostTest(
|
||||
mockMvc = mockMvc,
|
||||
endpoint = endpoint,
|
||||
body = request,
|
||||
) {
|
||||
status { isEqualTo(expectedError.httpStatus.value()) }
|
||||
jsonPath("$.code") { value(expectedError.errorCode) }
|
||||
}
|
||||
}
|
||||
|
||||
When("입력 값의 형식이 잘못되면 예외 응답") {
|
||||
val request = ThemeCreateRequest(
|
||||
name = "theme1",
|
||||
description = "description1",
|
||||
thumbnail = "http://example.com/thumbnail1.jpg"
|
||||
)
|
||||
|
||||
fun runTest(request: ThemeCreateRequest) {
|
||||
val expectedError = CommonErrorCode.INVALID_INPUT_VALUE
|
||||
|
||||
runPostTest(
|
||||
mockMvc = mockMvc,
|
||||
endpoint = endpoint,
|
||||
body = request,
|
||||
) {
|
||||
status { isEqualTo(expectedError.httpStatus.value()) }
|
||||
jsonPath("$.code") { value(expectedError.errorCode) }
|
||||
}
|
||||
}
|
||||
|
||||
Then("이름이 공백인 경우") {
|
||||
val invalidRequest = request.copy(name = " ")
|
||||
runTest(invalidRequest)
|
||||
}
|
||||
|
||||
Then("이름이 20글자를 초과하는 경우") {
|
||||
val invalidRequest = request.copy(name = "a".repeat(21))
|
||||
runTest(invalidRequest)
|
||||
}
|
||||
|
||||
Then("설명이 공백인 경우") {
|
||||
val invalidRequest = request.copy(description = " ")
|
||||
runTest(invalidRequest)
|
||||
}
|
||||
|
||||
Then("설명이 100글자를 초과하는 경우") {
|
||||
val invalidRequest = request.copy(description = "a".repeat(101))
|
||||
runTest(invalidRequest)
|
||||
}
|
||||
|
||||
Then("썸네일이 공백인 경우") {
|
||||
val invalidRequest = request.copy(thumbnail = " ")
|
||||
runTest(invalidRequest)
|
||||
}
|
||||
|
||||
Then("썸네일이 URL 형식이 아닌 경우") {
|
||||
val invalidRequest = request.copy(thumbnail = "invalid-url")
|
||||
runTest(invalidRequest)
|
||||
}
|
||||
}
|
||||
|
||||
Then("정상 응답") {
|
||||
val theme = ThemeFixture.create(
|
||||
id = 1,
|
||||
name = request.name,
|
||||
description = request.description,
|
||||
thumbnail = request.thumbnail
|
||||
)
|
||||
|
||||
every {
|
||||
themeService.createTheme(request)
|
||||
} returns theme.toCreateResponse()
|
||||
|
||||
runPostTest(
|
||||
mockMvc = mockMvc,
|
||||
endpoint = endpoint,
|
||||
body = request,
|
||||
) {
|
||||
status { isCreated() }
|
||||
header {
|
||||
string("Location", "/themes/${theme.id}")
|
||||
}
|
||||
jsonPath("$.data.id") { value(theme.id) }
|
||||
jsonPath("$.data.name") { value(theme.name) }
|
||||
jsonPath("$.data.description") { value(theme.description) }
|
||||
jsonPath("$.data.thumbnail") { value(theme.thumbnail) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Given("DELETE /themes/{id} 요청을") {
|
||||
val themeId = 1L
|
||||
val endpoint = "/themes/$themeId"
|
||||
|
||||
When("관리자가 아닌 사용자가 보내면 예외 응답") {
|
||||
Then("로그인 하지 않은 경우") {
|
||||
doNotLogin()
|
||||
|
||||
val expectedError = AuthErrorCode.MEMBER_NOT_FOUND
|
||||
runDeleteTest(
|
||||
mockMvc = mockMvc,
|
||||
endpoint = endpoint,
|
||||
) {
|
||||
status { isEqualTo(expectedError.httpStatus.value()) }
|
||||
jsonPath("$.code", equalTo(expectedError.errorCode))
|
||||
}
|
||||
}
|
||||
|
||||
Then("로그인은 하였으나 관리자가 아닌 경우") {
|
||||
loginAsUser()
|
||||
|
||||
val expectedError = AuthErrorCode.ACCESS_DENIED
|
||||
runDeleteTest(
|
||||
mockMvc = mockMvc,
|
||||
endpoint = endpoint,
|
||||
) {
|
||||
status { isEqualTo(expectedError.httpStatus.value()) }
|
||||
jsonPath("$.code", equalTo(expectedError.errorCode))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
When("관리자가 보낼 때") {
|
||||
beforeTest {
|
||||
loginAsAdmin()
|
||||
}
|
||||
|
||||
Then("이미 예약된 테마이면 예외 응답") {
|
||||
val expectedError = ThemeErrorCode.THEME_ALREADY_RESERVED
|
||||
every {
|
||||
themeService.deleteTheme(themeId)
|
||||
} throws ThemeException(expectedError)
|
||||
|
||||
runDeleteTest(
|
||||
mockMvc = mockMvc,
|
||||
endpoint = endpoint,
|
||||
) {
|
||||
status { isEqualTo(expectedError.httpStatus.value()) }
|
||||
jsonPath("$.code") { value(expectedError.errorCode) }
|
||||
}
|
||||
}
|
||||
|
||||
Then("정상 응답") {
|
||||
every { themeService.deleteTheme(themeId) } returns Unit
|
||||
|
||||
runDeleteTest(
|
||||
mockMvc = mockMvc,
|
||||
endpoint = endpoint,
|
||||
) {
|
||||
status { isNoContent() }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,204 +1,51 @@
|
||||
package roomescape.util
|
||||
|
||||
import com.github.f4b6a3.tsid.TsidFactory
|
||||
import roomescape.auth.infrastructure.jwt.JwtHandler
|
||||
import roomescape.auth.web.LoginRequest
|
||||
import roomescape.common.config.next
|
||||
import roomescape.member.infrastructure.persistence.MemberEntity
|
||||
import roomescape.member.infrastructure.persistence.Role
|
||||
import roomescape.payment.infrastructure.client.PaymentApproveRequest
|
||||
import roomescape.payment.infrastructure.client.PaymentApproveResponse
|
||||
import roomescape.payment.infrastructure.persistence.CanceledPaymentEntity
|
||||
import roomescape.payment.infrastructure.persistence.PaymentEntity
|
||||
import roomescape.payment.web.PaymentCancelRequest
|
||||
import roomescape.payment.web.PaymentCancelResponse
|
||||
import roomescape.reservation.infrastructure.persistence.ReservationEntity
|
||||
import roomescape.reservation.infrastructure.persistence.ReservationStatus
|
||||
import roomescape.reservation.web.AdminReservationCreateRequest
|
||||
import roomescape.reservation.web.ReservationCreateWithPaymentRequest
|
||||
import roomescape.reservation.web.WaitingCreateRequest
|
||||
import roomescape.theme.infrastructure.persistence.ThemeEntity
|
||||
import roomescape.time.infrastructure.persistence.TimeEntity
|
||||
import roomescape.schedule.web.ScheduleCreateRequest
|
||||
import roomescape.theme.infrastructure.persistence.v2.Difficulty
|
||||
import roomescape.theme.web.ThemeCreateRequestV2
|
||||
import java.time.LocalDate
|
||||
import java.time.LocalTime
|
||||
import java.time.OffsetDateTime
|
||||
|
||||
val TsidFactory: TsidFactory = TsidFactory(0)
|
||||
|
||||
object MemberFixture {
|
||||
const val NOT_LOGGED_IN_USERID: Long = 0
|
||||
|
||||
fun create(
|
||||
id: Long? = TsidFactory.next(),
|
||||
name: String = "sangdol",
|
||||
account: String = "default",
|
||||
password: String = "password",
|
||||
role: Role = Role.ADMIN
|
||||
): MemberEntity = MemberEntity(id, name, "$account@email.com", password, role)
|
||||
|
||||
fun admin(): MemberEntity = create(
|
||||
id = 2L,
|
||||
account = "admin",
|
||||
object MemberFixtureV2 {
|
||||
val admin: MemberEntity = MemberEntity(
|
||||
_id = 9304,
|
||||
name = "ADMIN",
|
||||
email = "admin@example.com",
|
||||
password = "adminPassword",
|
||||
role = Role.ADMIN
|
||||
)
|
||||
|
||||
fun adminLoginRequest(): LoginRequest = LoginRequest(
|
||||
email = admin().email,
|
||||
password = admin().password
|
||||
)
|
||||
|
||||
fun user(): MemberEntity = create(
|
||||
id = 1L,
|
||||
account = "user",
|
||||
val user: MemberEntity = MemberEntity(
|
||||
_id = 9305,
|
||||
name = "USER",
|
||||
email = "user@example.com",
|
||||
password = "userPassword",
|
||||
role = Role.MEMBER
|
||||
)
|
||||
}
|
||||
|
||||
fun userLoginRequest(): LoginRequest = LoginRequest(
|
||||
email = user().email,
|
||||
password = user().password
|
||||
object ThemeFixtureV2 {
|
||||
val createRequest: ThemeCreateRequestV2 = ThemeCreateRequestV2(
|
||||
name = "Matilda Green",
|
||||
description = "constituto",
|
||||
thumbnailUrl = "https://duckduckgo.com/?q=mediocrem",
|
||||
difficulty = Difficulty.VERY_EASY,
|
||||
price = 10000,
|
||||
minParticipants = 3,
|
||||
maxParticipants = 5,
|
||||
availableMinutes = 80,
|
||||
expectedMinutesFrom = 60,
|
||||
expectedMinutesTo = 70,
|
||||
isOpen = true
|
||||
)
|
||||
}
|
||||
|
||||
object TimeFixture {
|
||||
fun create(
|
||||
id: Long? = TsidFactory.next(),
|
||||
startAt: LocalTime = LocalTime.now().plusHours(1),
|
||||
): TimeEntity = TimeEntity(id, startAt)
|
||||
}
|
||||
|
||||
object ThemeFixture {
|
||||
fun create(
|
||||
id: Long? = TsidFactory.next(),
|
||||
name: String = "Default Theme",
|
||||
description: String = "Default Description",
|
||||
thumbnail: String = "https://example.com/default-thumbnail.jpg"
|
||||
): ThemeEntity = ThemeEntity(id, name, description, thumbnail)
|
||||
}
|
||||
|
||||
object ReservationFixture {
|
||||
fun create(
|
||||
id: Long? = TsidFactory.next(),
|
||||
date: LocalDate = LocalDate.now().plusWeeks(1),
|
||||
theme: ThemeEntity = ThemeFixture.create(),
|
||||
time: TimeEntity = TimeFixture.create(),
|
||||
member: MemberEntity = MemberFixture.create(),
|
||||
status: ReservationStatus = ReservationStatus.CONFIRMED_PAYMENT_REQUIRED
|
||||
): ReservationEntity = ReservationEntity(id, date, time, 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",
|
||||
): ReservationCreateWithPaymentRequest = ReservationCreateWithPaymentRequest(
|
||||
date = date,
|
||||
timeId = timeId,
|
||||
themeId = themeId,
|
||||
paymentKey = paymentKey,
|
||||
orderId = orderId,
|
||||
amount = amount,
|
||||
paymentType = paymentType
|
||||
)
|
||||
|
||||
fun createAdminRequest(
|
||||
date: LocalDate = LocalDate.now().plusWeeks(1),
|
||||
themeId: Long = 1L,
|
||||
timeId: Long = 1L,
|
||||
memberId: Long = 1L
|
||||
): AdminReservationCreateRequest = AdminReservationCreateRequest(
|
||||
date = date,
|
||||
timeId = timeId,
|
||||
themeId = themeId,
|
||||
memberId = memberId
|
||||
)
|
||||
|
||||
fun createWaitingRequest(
|
||||
date: LocalDate = LocalDate.now().plusWeeks(1),
|
||||
themeId: Long = 1L,
|
||||
timeId: Long = 1L
|
||||
): WaitingCreateRequest = WaitingCreateRequest(
|
||||
date = date,
|
||||
timeId = timeId,
|
||||
themeId = themeId
|
||||
object ScheduleFixture {
|
||||
val createRequest: ScheduleCreateRequest = ScheduleCreateRequest(
|
||||
date = LocalDate.now().plusDays(1),
|
||||
time = LocalTime.now(),
|
||||
themeId = 1L
|
||||
)
|
||||
}
|
||||
|
||||
object JwtFixture {
|
||||
const val SECRET_KEY_STRING: String = "daijawligagaf@LIJ$@U)9nagnalkkgalijaddljfi"
|
||||
const val EXPIRATION_TIME: Long = 1000 * 60 * 60
|
||||
|
||||
fun create(
|
||||
secretKey: String = SECRET_KEY_STRING,
|
||||
expirationTime: Long = EXPIRATION_TIME
|
||||
): JwtHandler = JwtHandler(secretKey, expirationTime)
|
||||
}
|
||||
|
||||
object PaymentFixture {
|
||||
const val PAYMENT_KEY: String = "paymentKey"
|
||||
const val ORDER_ID: String = "orderId"
|
||||
const val AMOUNT: Long = 10000L
|
||||
|
||||
fun create(
|
||||
id: Long? = TsidFactory.next(),
|
||||
orderId: String = ORDER_ID,
|
||||
paymentKey: String = PAYMENT_KEY,
|
||||
totalAmount: Long = AMOUNT,
|
||||
reservation: ReservationEntity = ReservationFixture.create(id = 1L),
|
||||
approvedAt: OffsetDateTime = OffsetDateTime.now()
|
||||
): PaymentEntity = PaymentEntity(
|
||||
_id = id,
|
||||
orderId = orderId,
|
||||
paymentKey = paymentKey,
|
||||
totalAmount = totalAmount,
|
||||
reservation = reservation,
|
||||
approvedAt = approvedAt
|
||||
)
|
||||
|
||||
fun createCanceled(
|
||||
id: Long? = TsidFactory.next(),
|
||||
paymentKey: String = PAYMENT_KEY,
|
||||
cancelReason: String = "Test Cancel",
|
||||
cancelAmount: Long = AMOUNT,
|
||||
approvedAt: OffsetDateTime = OffsetDateTime.now(),
|
||||
canceledAt: OffsetDateTime = approvedAt.plusHours(1)
|
||||
): CanceledPaymentEntity = CanceledPaymentEntity(
|
||||
_id = id,
|
||||
paymentKey = paymentKey,
|
||||
cancelReason = cancelReason,
|
||||
cancelAmount = cancelAmount,
|
||||
approvedAt = approvedAt,
|
||||
canceledAt = canceledAt
|
||||
|
||||
)
|
||||
|
||||
fun createApproveRequest(): PaymentApproveRequest = PaymentApproveRequest(
|
||||
paymentKey = PAYMENT_KEY,
|
||||
orderId = ORDER_ID,
|
||||
amount = AMOUNT,
|
||||
paymentType = "CARD"
|
||||
)
|
||||
|
||||
fun createApproveResponse(): PaymentApproveResponse = PaymentApproveResponse(
|
||||
paymentKey = PAYMENT_KEY,
|
||||
orderId = ORDER_ID,
|
||||
approvedAt = OffsetDateTime.now(),
|
||||
totalAmount = AMOUNT
|
||||
)
|
||||
|
||||
fun createCancelRequest(): PaymentCancelRequest = PaymentCancelRequest(
|
||||
paymentKey = PAYMENT_KEY,
|
||||
amount = AMOUNT,
|
||||
cancelReason = "Test Cancel"
|
||||
)
|
||||
|
||||
fun createCancelResponse(): PaymentCancelResponse = PaymentCancelResponse(
|
||||
cancelStatus = "SUCCESS",
|
||||
cancelReason = "Test Cancel",
|
||||
cancelAmount = AMOUNT,
|
||||
canceledAt = OffsetDateTime.now().plusMinutes(1)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@ -1,51 +0,0 @@
|
||||
package roomescape.util
|
||||
|
||||
import roomescape.member.infrastructure.persistence.MemberEntity
|
||||
import roomescape.member.infrastructure.persistence.Role
|
||||
import roomescape.schedule.web.ScheduleCreateRequest
|
||||
import roomescape.theme.infrastructure.persistence.v2.Difficulty
|
||||
import roomescape.theme.web.ThemeCreateRequestV2
|
||||
import java.time.LocalDate
|
||||
import java.time.LocalTime
|
||||
|
||||
object MemberFixtureV2 {
|
||||
val admin: MemberEntity = MemberEntity(
|
||||
_id = 9304,
|
||||
name = "ADMIN",
|
||||
email = "admin@example.com",
|
||||
password = "adminPassword",
|
||||
role = Role.ADMIN
|
||||
)
|
||||
|
||||
val user: MemberEntity = MemberEntity(
|
||||
_id = 9305,
|
||||
name = "USER",
|
||||
email = "user@example.com",
|
||||
password = "userPassword",
|
||||
role = Role.MEMBER
|
||||
)
|
||||
}
|
||||
|
||||
object ThemeFixtureV2 {
|
||||
val createRequest: ThemeCreateRequestV2 = ThemeCreateRequestV2(
|
||||
name = "Matilda Green",
|
||||
description = "constituto",
|
||||
thumbnailUrl = "https://duckduckgo.com/?q=mediocrem",
|
||||
difficulty = Difficulty.VERY_EASY,
|
||||
price = 10000,
|
||||
minParticipants = 3,
|
||||
maxParticipants = 5,
|
||||
availableMinutes = 80,
|
||||
expectedMinutesFrom = 60,
|
||||
expectedMinutesTo = 70,
|
||||
isOpen = true
|
||||
)
|
||||
}
|
||||
|
||||
object ScheduleFixture {
|
||||
val createRequest: ScheduleCreateRequest = ScheduleCreateRequest(
|
||||
date = LocalDate.now().plusDays(1),
|
||||
time = LocalTime.now(),
|
||||
themeId = 1L
|
||||
)
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user