feat: 통합 테스트 전환 & API 기능 변경으로 인한 기존 테스트 제거

This commit is contained in:
이상진 2025-09-07 22:08:40 +09:00
parent 11fd345d5e
commit e7f69aaee4
17 changed files with 33 additions and 2500 deletions

View File

@ -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()
}

View File

@ -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
}
}
}
})

View File

@ -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
}
}
}
})

View File

@ -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
}
}
}
})

View File

@ -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
}
}
}
})

View File

@ -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
}
}
}
})

View File

@ -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()
}
}
}

View File

@ -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
}
}
}
})

View File

@ -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
}
}
})

View File

@ -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)
}
}
}
})

View File

@ -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)
}
}
}
})

View File

@ -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
}
}
}
}

View File

@ -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
}
}

View File

@ -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))
}
}
}
}
}

View File

@ -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() }
}
}
}
}
}
}

View File

@ -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)
)
}

View File

@ -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
)
}