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
|
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.MemberEntity
|
||||||
import roomescape.member.infrastructure.persistence.Role
|
import roomescape.member.infrastructure.persistence.Role
|
||||||
import roomescape.payment.infrastructure.client.PaymentApproveRequest
|
import roomescape.schedule.web.ScheduleCreateRequest
|
||||||
import roomescape.payment.infrastructure.client.PaymentApproveResponse
|
import roomescape.theme.infrastructure.persistence.v2.Difficulty
|
||||||
import roomescape.payment.infrastructure.persistence.CanceledPaymentEntity
|
import roomescape.theme.web.ThemeCreateRequestV2
|
||||||
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 java.time.LocalDate
|
import java.time.LocalDate
|
||||||
import java.time.LocalTime
|
import java.time.LocalTime
|
||||||
import java.time.OffsetDateTime
|
|
||||||
|
|
||||||
val TsidFactory: TsidFactory = TsidFactory(0)
|
object MemberFixtureV2 {
|
||||||
|
val admin: MemberEntity = MemberEntity(
|
||||||
object MemberFixture {
|
_id = 9304,
|
||||||
const val NOT_LOGGED_IN_USERID: Long = 0
|
name = "ADMIN",
|
||||||
|
email = "admin@example.com",
|
||||||
fun create(
|
password = "adminPassword",
|
||||||
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",
|
|
||||||
role = Role.ADMIN
|
role = Role.ADMIN
|
||||||
)
|
)
|
||||||
|
|
||||||
fun adminLoginRequest(): LoginRequest = LoginRequest(
|
val user: MemberEntity = MemberEntity(
|
||||||
email = admin().email,
|
_id = 9305,
|
||||||
password = admin().password
|
name = "USER",
|
||||||
)
|
email = "user@example.com",
|
||||||
|
password = "userPassword",
|
||||||
fun user(): MemberEntity = create(
|
|
||||||
id = 1L,
|
|
||||||
account = "user",
|
|
||||||
role = Role.MEMBER
|
role = Role.MEMBER
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fun userLoginRequest(): LoginRequest = LoginRequest(
|
object ThemeFixtureV2 {
|
||||||
email = user().email,
|
val createRequest: ThemeCreateRequestV2 = ThemeCreateRequestV2(
|
||||||
password = user().password
|
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 {
|
object ScheduleFixture {
|
||||||
fun create(
|
val createRequest: ScheduleCreateRequest = ScheduleCreateRequest(
|
||||||
id: Long? = TsidFactory.next(),
|
date = LocalDate.now().plusDays(1),
|
||||||
startAt: LocalTime = LocalTime.now().plusHours(1),
|
time = LocalTime.now(),
|
||||||
): TimeEntity = TimeEntity(id, startAt)
|
themeId = 1L
|
||||||
}
|
|
||||||
|
|
||||||
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 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