test: PaymentServiceTest 코틀린 전환

This commit is contained in:
이상진 2025-07-21 21:01:56 +09:00
parent ef76d1b003
commit ba0dc44e04
2 changed files with 107 additions and 263 deletions

View File

@ -1,129 +0,0 @@
package roomescape.payment.business
import io.kotest.assertions.assertSoftly
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.just
import io.mockk.mockk
import io.mockk.runs
import org.springframework.http.HttpStatus
import roomescape.common.exception.ErrorType
import roomescape.common.exception.RoomescapeException
import roomescape.payment.infrastructure.persistence.CanceledPaymentRepository
import roomescape.payment.infrastructure.persistence.PaymentRepository
import roomescape.payment.web.PaymentCancel
import roomescape.util.PaymentFixture
import java.time.OffsetDateTime
class PaymentServiceKTest : FunSpec({
val paymentRepository: PaymentRepository = mockk()
val canceledPaymentRepository: CanceledPaymentRepository = mockk()
val paymentService = PaymentService(paymentRepository, canceledPaymentRepository)
context("cancelPaymentByAdmin") {
val reservationId = 1L
test("reservationId로 paymentKey를 찾을 수 없으면 예외를 던진다.") {
every { paymentRepository.findPaymentKeyByReservationId(reservationId) } returns null
val exception = shouldThrow<RoomescapeException> {
paymentService.cancelPaymentByAdmin(reservationId)
}
assertSoftly(exception) {
this.errorType shouldBe ErrorType.PAYMENT_NOT_FOUND
this.httpStatus shouldBe HttpStatus.NOT_FOUND
}
}
context("reservationId로 paymentKey를 찾고난 후") {
val paymentKey = "test-payment-key"
every {
paymentRepository.findPaymentKeyByReservationId(reservationId)
} returns paymentKey
test("해당 paymentKey로 paymentEntity를 찾을 수 없으면 예외를 던진다.") {
every {
paymentRepository.findByPaymentKey(paymentKey)
} returns null
val exception = shouldThrow<RoomescapeException> {
paymentService.cancelPaymentByAdmin(reservationId)
}
assertSoftly(exception) {
this.errorType shouldBe ErrorType.PAYMENT_NOT_FOUND
this.httpStatus shouldBe HttpStatus.NOT_FOUND
}
}
test("해당 paymentKey로 paymentEntity를 찾고, cancelPaymentEntity를 저장한다.") {
val paymentEntity = PaymentFixture.create(paymentKey = paymentKey)
every {
paymentRepository.findByPaymentKey(paymentKey)
} returns paymentEntity.also {
every {
paymentRepository.delete(it)
} just runs
}
every {
canceledPaymentRepository.save(any())
} returns PaymentFixture.createCanceled(
id = 1L,
paymentKey = paymentKey,
cancelAmount = paymentEntity.totalAmount,
)
val result: PaymentCancel.Request = paymentService.cancelPaymentByAdmin(reservationId)
assertSoftly(result) {
this.paymentKey shouldBe paymentKey
this.amount shouldBe paymentEntity.totalAmount
this.cancelReason shouldBe "고객 요청"
}
}
}
}
context("updateCanceledTime") {
val paymentKey = "test-payment-key"
val canceledAt = OffsetDateTime.now()
test("paymentKey로 canceledPaymentEntity를 찾을 수 없으면 예외를 던진다.") {
every {
canceledPaymentRepository.findByPaymentKey(paymentKey)
} returns null
val exception = shouldThrow<RoomescapeException> {
paymentService.updateCanceledTime(paymentKey, canceledAt)
}
assertSoftly(exception) {
this.errorType shouldBe ErrorType.PAYMENT_NOT_FOUND
this.httpStatus shouldBe HttpStatus.NOT_FOUND
}
}
test("paymentKey로 canceledPaymentEntity를 찾고, canceledAt을 업데이트한다.") {
val canceledPaymentEntity = PaymentFixture.createCanceled(
paymentKey = paymentKey,
canceledAt = canceledAt.minusMinutes(1)
)
every {
canceledPaymentRepository.findByPaymentKey(paymentKey)
} returns canceledPaymentEntity
paymentService.updateCanceledTime(paymentKey, canceledAt)
assertSoftly(canceledPaymentEntity) {
this.canceledAt shouldBe canceledAt
}
}
}
})

View File

@ -1,156 +1,129 @@
package roomescape.payment.business; package roomescape.payment.business
import static org.assertj.core.api.Assertions.*; import io.kotest.assertions.assertSoftly
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.just
import io.mockk.mockk
import io.mockk.runs
import org.springframework.http.HttpStatus
import roomescape.common.exception.ErrorType
import roomescape.common.exception.RoomescapeException
import roomescape.payment.infrastructure.persistence.CanceledPaymentRepository
import roomescape.payment.infrastructure.persistence.PaymentRepository
import roomescape.payment.web.PaymentCancel
import roomescape.util.PaymentFixture
import java.time.OffsetDateTime
import java.time.LocalDate; class PaymentServiceTest : FunSpec({
import java.time.LocalDateTime; val paymentRepository: PaymentRepository = mockk()
import java.time.OffsetDateTime; val canceledPaymentRepository: CanceledPaymentRepository = mockk()
import org.junit.jupiter.api.DisplayName; val paymentService = PaymentService(paymentRepository, canceledPaymentRepository)
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.jdbc.Sql;
import org.springframework.test.context.jdbc.Sql.ExecutionPhase;
import roomescape.common.exception.RoomescapeException; context("cancelPaymentByAdmin") {
import roomescape.member.infrastructure.persistence.MemberEntity; val reservationId = 1L
import roomescape.member.infrastructure.persistence.MemberRepository; test("reservationId로 paymentKey를 찾을 수 없으면 예외를 던진다.") {
import roomescape.member.infrastructure.persistence.Role; every { paymentRepository.findPaymentKeyByReservationId(reservationId) } returns null
import roomescape.payment.infrastructure.persistence.CanceledPaymentEntity;
import roomescape.payment.infrastructure.persistence.CanceledPaymentRepository;
import roomescape.payment.web.PaymentApprove;
import roomescape.payment.web.PaymentCancel;
import roomescape.payment.web.ReservationPaymentResponse;
import roomescape.reservation.infrastructure.persistence.ReservationEntity;
import roomescape.reservation.infrastructure.persistence.ReservationRepository;
import roomescape.reservation.infrastructure.persistence.ReservationStatus;
import roomescape.reservation.infrastructure.persistence.ReservationTimeEntity;
import roomescape.reservation.infrastructure.persistence.ReservationTimeRepository;
import roomescape.theme.infrastructure.persistence.ThemeEntity;
import roomescape.theme.infrastructure.persistence.ThemeRepository;
@SpringBootTest val exception = shouldThrow<RoomescapeException> {
@Sql(scripts = "/truncate.sql", executionPhase = ExecutionPhase.BEFORE_TEST_METHOD) paymentService.cancelPaymentByAdmin(reservationId)
class PaymentServiceTest { }
@Autowired assertSoftly(exception) {
private PaymentService paymentService; this.errorType shouldBe ErrorType.PAYMENT_NOT_FOUND
@Autowired this.httpStatus shouldBe HttpStatus.NOT_FOUND
private ReservationRepository reservationRepository; }
@Autowired }
private MemberRepository memberRepository;
@Autowired
private ReservationTimeRepository reservationTimeRepository;
@Autowired
private ThemeRepository themeRepository;
@Autowired
private CanceledPaymentRepository canceledPaymentRepository;
@Test context("reservationId로 paymentKey를 찾고난 후") {
@DisplayName("결제 정보를 저장한다.") val paymentKey = "test-payment-key"
void savePayment() {
// given
PaymentApprove.Response paymentInfo = new PaymentApprove.Response("payment-key", "order-id",
OffsetDateTime.now(), 10000L);
LocalDateTime localDateTime = LocalDateTime.now().plusHours(1L);
LocalDate date = localDateTime.toLocalDate();
ReservationTimeEntity time = reservationTimeRepository.save(
new ReservationTimeEntity(null, localDateTime.toLocalTime()));
MemberEntity member = memberRepository.save(
new MemberEntity(null, "member", "email@email.com", "password", Role.MEMBER));
ThemeEntity theme = themeRepository.save(new ThemeEntity(null, "name", "desc", "thumbnail"));
ReservationEntity reservation = reservationRepository.save(
new ReservationEntity(null, date, time, theme, member,
ReservationStatus.CONFIRMED));
// when every {
ReservationPaymentResponse reservationPaymentResponse = paymentService.savePayment(paymentInfo, reservation); paymentRepository.findPaymentKeyByReservationId(reservationId)
} returns paymentKey
// then test("해당 paymentKey로 paymentEntity를 찾을 수 없으면 예외를 던진다.") {
assertThat(reservationPaymentResponse.reservation().id).isEqualTo(reservation.getId()); every {
assertThat(reservationPaymentResponse.paymentKey()).isEqualTo(paymentInfo.paymentKey); paymentRepository.findByPaymentKey(paymentKey)
} } returns null
@Test val exception = shouldThrow<RoomescapeException> {
@DisplayName("예약 ID로 결제 정보를 제거하고, 결제 취소 테이블에 취소 정보를 저장한다.") paymentService.cancelPaymentByAdmin(reservationId)
void cancelPaymentByAdmin() { }
// given
PaymentApprove.Response paymentInfo = new PaymentApprove.Response("payment-key", "order-id",
OffsetDateTime.now(), 10000L);
LocalDateTime localDateTime = LocalDateTime.now().plusHours(1L);
LocalDate date = localDateTime.toLocalDate();
ReservationTimeEntity time = reservationTimeRepository.save(
new ReservationTimeEntity(null, localDateTime.toLocalTime()));
MemberEntity member = memberRepository.save(
new MemberEntity(null, "member", "email@email.com", "password", Role.MEMBER));
ThemeEntity theme = themeRepository.save(new ThemeEntity(null, "name", "desc", "thumbnail"));
ReservationEntity reservation = reservationRepository.save(
new ReservationEntity(null, date, time, theme, member,
ReservationStatus.CONFIRMED));
paymentService.savePayment(paymentInfo, reservation); assertSoftly(exception) {
this.errorType shouldBe ErrorType.PAYMENT_NOT_FOUND
this.httpStatus shouldBe HttpStatus.NOT_FOUND
}
}
// when test("해당 paymentKey로 paymentEntity를 찾고, cancelPaymentEntity를 저장한다.") {
PaymentCancel.Request paymentCancelRequest = paymentService.cancelPaymentByAdmin(reservation.getId()); val paymentEntity = PaymentFixture.create(paymentKey = paymentKey)
// then every {
assertThat(canceledPaymentRepository.findByPaymentKey("payment-key")).isNotNull(); paymentRepository.findByPaymentKey(paymentKey)
assertThat(paymentCancelRequest.paymentKey).isEqualTo(paymentInfo.paymentKey); } returns paymentEntity.also {
assertThat(paymentCancelRequest.cancelReason).isEqualTo("고객 요청"); every {
assertThat(paymentCancelRequest.amount).isEqualTo(10000L); paymentRepository.delete(it)
} } just runs
}
@Test every {
@DisplayName("입력된 예약 ID에 대한 결제 정보가 없으면 예외가 발생한다.") canceledPaymentRepository.save(any())
void cancelPaymentByAdminWithNonExistentReservationId() { } returns PaymentFixture.createCanceled(
// given id = 1L,
Long nonExistentReservationId = 1L; paymentKey = paymentKey,
cancelAmount = paymentEntity.totalAmount,
)
// when val result: PaymentCancel.Request = paymentService.cancelPaymentByAdmin(reservationId)
assertThatThrownBy(() -> paymentService.cancelPaymentByAdmin(nonExistentReservationId))
.isInstanceOf(RoomescapeException.class);
}
@Test assertSoftly(result) {
@DisplayName("결제 취소 정보에 있는 취소 시간을 업데이트한다.") this.paymentKey shouldBe paymentKey
void updateCanceledTime() { this.amount shouldBe paymentEntity.totalAmount
// given this.cancelReason shouldBe "고객 요청"
PaymentApprove.Response paymentInfo = new PaymentApprove.Response("payment-key", "order-id", }
OffsetDateTime.now(), 10000L); }
LocalDateTime localDateTime = LocalDateTime.now().plusHours(1L); }
LocalDate date = localDateTime.toLocalDate(); }
ReservationTimeEntity time = reservationTimeRepository.save(
new ReservationTimeEntity(null, localDateTime.toLocalTime()));
MemberEntity member = memberRepository.save(
new MemberEntity(null, "member", "email@email.com", "password", Role.MEMBER));
ThemeEntity theme = themeRepository.save(new ThemeEntity(null, "name", "desc", "thumbnail"));
ReservationEntity reservation = reservationRepository.save(
new ReservationEntity(null, date, time, theme, member,
ReservationStatus.CONFIRMED));
paymentService.savePayment(paymentInfo, reservation); context("updateCanceledTime") {
paymentService.cancelPaymentByAdmin(reservation.getId()); val paymentKey = "test-payment-key"
val canceledAt = OffsetDateTime.now()
// when test("paymentKey로 canceledPaymentEntity를 찾을 수 없으면 예외를 던진다.") {
OffsetDateTime canceledAt = OffsetDateTime.now().plusHours(2L); every {
paymentService.updateCanceledTime(paymentInfo.paymentKey, canceledAt); canceledPaymentRepository.findByPaymentKey(paymentKey)
} returns null
// then val exception = shouldThrow<RoomescapeException> {
CanceledPaymentEntity canceledPayment = canceledPaymentRepository.findByPaymentKey(paymentInfo.paymentKey); paymentService.updateCanceledTime(paymentKey, canceledAt)
}
assertThat(canceledPayment).isNotNull(); assertSoftly(exception) {
assertThat(canceledPayment.getCanceledAt()).isEqualTo(canceledAt); this.errorType shouldBe ErrorType.PAYMENT_NOT_FOUND
} this.httpStatus shouldBe HttpStatus.NOT_FOUND
}
}
@Test test("paymentKey로 canceledPaymentEntity를 찾고, canceledAt을 업데이트한다.") {
@DisplayName("결제 취소 시간을 업데이트 할 때, 입력한 paymentKey가 존재하지 않으면 예외가 발생한다.") val canceledPaymentEntity = PaymentFixture.createCanceled(
void updateCanceledTimeWithNonExistentPaymentKey() { paymentKey = paymentKey,
// given canceledAt = canceledAt.minusMinutes(1)
OffsetDateTime canceledAt = OffsetDateTime.now().plusHours(2L); )
// when every {
assertThatThrownBy(() -> paymentService.updateCanceledTime("non-existent-payment-key", canceledAt)) canceledPaymentRepository.findByPaymentKey(paymentKey)
.isInstanceOf(RoomescapeException.class); } returns canceledPaymentEntity
}
} paymentService.updateCanceledTime(paymentKey, canceledAt)
assertSoftly(canceledPaymentEntity) {
this.canceledAt shouldBe canceledAt
}
}
}
})