diff --git a/src/main/java/roomescape/payment/web/PaymentDTO.kt b/src/main/java/roomescape/payment/web/PaymentDTO.kt index a6261cce..b346c20c 100644 --- a/src/main/java/roomescape/payment/web/PaymentDTO.kt +++ b/src/main/java/roomescape/payment/web/PaymentDTO.kt @@ -5,6 +5,7 @@ import com.fasterxml.jackson.databind.annotation.JsonDeserialize import roomescape.payment.infrastructure.client.PaymentCancelResponseDeserializer import roomescape.payment.infrastructure.persistence.PaymentEntity import roomescape.reservation.web.ReservationResponse +import roomescape.reservation.web.toResponse import java.time.OffsetDateTime class PaymentApprove { @@ -60,6 +61,6 @@ fun PaymentEntity.toReservationPaymentResponse(): ReservationPaymentResponse = R orderId = this.orderId, paymentKey = this.paymentKey, totalAmount = this.totalAmount, - reservation = ReservationResponse.from(this.reservation), + reservation = this.reservation.toResponse(), approvedAt = this.approvedAt ) \ No newline at end of file diff --git a/src/main/java/roomescape/reservation/business/ReservationWithPaymentService.kt b/src/main/java/roomescape/reservation/business/ReservationWithPaymentService.kt index 13a78758..a3b0c47b 100644 --- a/src/main/java/roomescape/reservation/business/ReservationWithPaymentService.kt +++ b/src/main/java/roomescape/reservation/business/ReservationWithPaymentService.kt @@ -1,56 +1,58 @@ -package roomescape.reservation.business; +package roomescape.reservation.business -import java.time.OffsetDateTime; - -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import roomescape.payment.business.PaymentService; -import roomescape.payment.web.PaymentApprove; -import roomescape.payment.web.PaymentCancel; -import roomescape.payment.web.ReservationPaymentResponse; -import roomescape.reservation.infrastructure.persistence.ReservationEntity; -import roomescape.reservation.web.ReservationRequest; -import roomescape.reservation.web.ReservationResponse; +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional +import roomescape.payment.business.PaymentService +import roomescape.payment.web.PaymentApprove +import roomescape.payment.web.PaymentCancel +import roomescape.reservation.infrastructure.persistence.ReservationEntity +import roomescape.reservation.web.ReservationRequest +import roomescape.reservation.web.ReservationResponse +import java.time.OffsetDateTime @Service @Transactional -public class ReservationWithPaymentService { +class ReservationWithPaymentService( + private val reservationService: ReservationService, + private val paymentService: PaymentService +) { + fun addReservationWithPayment( + request: ReservationRequest, + paymentInfo: PaymentApprove.Response, + memberId: Long + ): ReservationResponse { + val reservation: ReservationEntity = reservationService.addReservation(request, memberId) - private final ReservationService reservationService; - private final PaymentService paymentService; + return paymentService.savePayment(paymentInfo, reservation) + .reservation + } - public ReservationWithPaymentService(ReservationService reservationService, - PaymentService paymentService) { - this.reservationService = reservationService; - this.paymentService = paymentService; - } + fun saveCanceledPayment( + cancelInfo: PaymentCancel.Response, + approvedAt: OffsetDateTime, + paymentKey: String + ) { + paymentService.saveCanceledPayment(cancelInfo, approvedAt, paymentKey) + } - public ReservationResponse addReservationWithPayment(ReservationRequest request, - PaymentApprove.Response paymentInfo, - Long memberId) { - ReservationEntity reservation = reservationService.addReservation(request, memberId); - ReservationPaymentResponse reservationPaymentResponse = paymentService.savePayment(paymentInfo, reservation); + fun removeReservationWithPayment( + reservationId: Long, + memberId: Long + ): PaymentCancel.Request { + val paymentCancelRequest = paymentService.cancelPaymentByAdmin(reservationId) + reservationService.removeReservationById(reservationId, memberId) - return reservationPaymentResponse.reservation(); - } + return paymentCancelRequest + } - public void saveCanceledPayment(PaymentCancel.Response cancelInfo, OffsetDateTime approvedAt, String paymentKey) { - paymentService.saveCanceledPayment(cancelInfo, approvedAt, paymentKey); - } + @Transactional(readOnly = true) + fun isNotPaidReservation(reservationId: Long): Boolean = !paymentService.isReservationPaid(reservationId) - public PaymentCancel.Request removeReservationWithPayment(Long reservationId, Long memberId) { - PaymentCancel.Request paymentCancelRequest = paymentService.cancelPaymentByAdmin(reservationId); - reservationService.removeReservationById(reservationId, memberId); - return paymentCancelRequest; - } - @Transactional(readOnly = true) - public boolean isNotPaidReservation(Long reservationId) { - return !paymentService.isReservationPaid(reservationId); - } - - public void updateCanceledTime(String paymentKey, OffsetDateTime canceledAt) { - paymentService.updateCanceledTime(paymentKey, canceledAt); - } + fun updateCanceledTime( + paymentKey: String, + canceledAt: OffsetDateTime + ) { + paymentService.updateCanceledTime(paymentKey, canceledAt) + } } diff --git a/src/main/java/roomescape/reservation/web/ReservationResponse.kt b/src/main/java/roomescape/reservation/web/ReservationResponse.kt index 35e6eb36..158f54f6 100644 --- a/src/main/java/roomescape/reservation/web/ReservationResponse.kt +++ b/src/main/java/roomescape/reservation/web/ReservationResponse.kt @@ -3,7 +3,6 @@ package roomescape.reservation.web import com.fasterxml.jackson.annotation.JsonProperty import io.swagger.v3.oas.annotations.media.Schema import roomescape.member.web.MemberResponse -import roomescape.member.web.MemberResponse.Companion.fromEntity import roomescape.member.web.toResponse import roomescape.reservation.infrastructure.persistence.ReservationEntity import roomescape.reservation.infrastructure.persistence.ReservationStatus @@ -78,9 +77,9 @@ data class ReservationResponse( return ReservationResponse( reservation.id!!, reservation.date, - fromEntity(reservation.member), - ReservationTimeResponse.Companion.from(reservation.reservationTime), - ThemeResponse.Companion.from(reservation.theme), + reservation.member.toResponse(), + reservation.reservationTime.toResponse(), + reservation.theme.toResponse(), reservation.reservationStatus ) } diff --git a/src/test/java/roomescape/payment/infrastructure/persistence/PaymentRepositoryTest.kt b/src/test/java/roomescape/payment/infrastructure/persistence/PaymentRepositoryTest.kt index f398564d..4b202077 100644 --- a/src/test/java/roomescape/payment/infrastructure/persistence/PaymentRepositoryTest.kt +++ b/src/test/java/roomescape/payment/infrastructure/persistence/PaymentRepositoryTest.kt @@ -6,6 +6,7 @@ import io.kotest.matchers.shouldBe import jakarta.persistence.EntityManager import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest +import roomescape.reservation.infrastructure.persistence.ReservationEntity import roomescape.util.PaymentFixture import roomescape.util.ReservationFixture @@ -15,23 +16,23 @@ class PaymentRepositoryTest( @Autowired val entityManager: EntityManager ) : FunSpec() { - var reservationId: Long = 0L + lateinit var reservation: ReservationEntity init { context("existsByReservationId") { beforeTest { - reservationId = setupReservation() - PaymentFixture.create(reservationId = reservationId) + reservation = setupReservation() + PaymentFixture.create(reservation = reservation) .also { paymentRepository.save(it) } } test("true") { - paymentRepository.existsByReservationId(reservationId) + paymentRepository.existsByReservationId(reservation.id!!) .also { it shouldBe true } } test("false") { - paymentRepository.existsByReservationId(reservationId + 1) + paymentRepository.existsByReservationId(reservation.id!! + 1L) .also { it shouldBe false } } } @@ -40,20 +41,20 @@ class PaymentRepositoryTest( lateinit var paymentKey: String beforeTest { - reservationId = setupReservation() - paymentKey = PaymentFixture.create(reservationId = reservationId) + reservation = setupReservation() + paymentKey = PaymentFixture.create(reservation = reservation) .also { paymentRepository.save(it) } .paymentKey } test("정상 반환") { - paymentRepository.findPaymentKeyByReservationId(reservationId) + paymentRepository.findPaymentKeyByReservationId(reservation.id!!) ?.let { it shouldBe paymentKey } ?: throw AssertionError("Unexpected null value") } test("null 반환") { - paymentRepository.findPaymentKeyByReservationId(reservationId + 1) + paymentRepository.findPaymentKeyByReservationId(reservation.id!! + 1) .also { it shouldBe null } } } @@ -62,8 +63,8 @@ class PaymentRepositoryTest( lateinit var payment: PaymentEntity beforeTest { - reservationId = setupReservation() - payment = PaymentFixture.create(reservationId = reservationId) + reservation = setupReservation() + payment = PaymentFixture.create(reservation = reservation) .also { paymentRepository.save(it) } } @@ -89,7 +90,7 @@ class PaymentRepositoryTest( } } - private fun setupReservation(): Long { + private fun setupReservation(): ReservationEntity { return ReservationFixture.create().also { entityManager.persist(it.member) entityManager.persist(it.theme) @@ -98,6 +99,6 @@ class PaymentRepositoryTest( entityManager.flush() entityManager.clear() - }.id!! + } } } diff --git a/src/test/java/roomescape/reservation/business/ReservationWithPaymentServiceTest.kt b/src/test/java/roomescape/reservation/business/ReservationWithPaymentServiceTest.kt index 06942c89..d4e1e804 100644 --- a/src/test/java/roomescape/reservation/business/ReservationWithPaymentServiceTest.kt +++ b/src/test/java/roomescape/reservation/business/ReservationWithPaymentServiceTest.kt @@ -1,170 +1,122 @@ -package roomescape.reservation.business; +package roomescape.reservation.business -import static org.assertj.core.api.Assertions.*; +import io.kotest.assertions.assertSoftly +import io.kotest.core.spec.style.FunSpec +import io.kotest.matchers.shouldBe +import io.mockk.Runs +import io.mockk.every +import io.mockk.just +import io.mockk.mockk +import roomescape.payment.business.PaymentService +import roomescape.payment.infrastructure.persistence.PaymentEntity +import roomescape.payment.web.PaymentCancel +import roomescape.payment.web.toReservationPaymentResponse +import roomescape.reservation.infrastructure.persistence.ReservationEntity +import roomescape.reservation.infrastructure.persistence.ReservationStatus +import roomescape.reservation.web.ReservationRequest +import roomescape.reservation.web.ReservationResponse +import roomescape.util.* -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.time.OffsetDateTime; +class ReservationWithPaymentServiceTest : FunSpec({ + val reservationService: ReservationService = mockk() + val paymentService: PaymentService = mockk() -import org.junit.jupiter.api.DisplayName; -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; + val reservationWithPaymentService = ReservationWithPaymentService( + reservationService = reservationService, + paymentService = paymentService + ) -import roomescape.member.infrastructure.persistence.MemberEntity; -import roomescape.member.infrastructure.persistence.MemberRepository; -import roomescape.member.infrastructure.persistence.Role; -import roomescape.payment.infrastructure.persistence.CanceledPaymentRepository; -import roomescape.payment.infrastructure.persistence.PaymentEntity; -import roomescape.payment.infrastructure.persistence.PaymentRepository; -import roomescape.payment.web.PaymentApprove; -import roomescape.payment.web.PaymentCancel; -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.reservation.web.ReservationRequest; -import roomescape.reservation.web.ReservationResponse; -import roomescape.theme.infrastructure.persistence.ThemeEntity; -import roomescape.theme.infrastructure.persistence.ThemeRepository; + val reservationRequest: ReservationRequest = ReservationFixture.createRequest() + val paymentApproveResponse = PaymentFixture.createApproveResponse() + val memberId = 1L + val reservationEntity: ReservationEntity = ReservationFixture.create( + id = 1L, + date = reservationRequest.date, + reservationTime = ReservationTimeFixture.create(id = reservationRequest.timeId), + theme = ThemeFixture.create(id = reservationRequest.themeId), + member = MemberFixture.create(id = memberId), + status = ReservationStatus.CONFIRMED + ) + val paymentEntity: PaymentEntity = PaymentFixture.create( + id = 1L, + orderId = reservationRequest.orderId, + paymentKey = reservationRequest.paymentKey, + totalAmount = reservationRequest.amount, + reservation = reservationEntity, + ) -@SpringBootTest -@Sql(scripts = "/truncate.sql", executionPhase = ExecutionPhase.BEFORE_TEST_METHOD) -class ReservationWithPaymentServiceTest { + context("addReservationWithPayment") { + test("예약 및 결제 정보를 저장한다.") { + every { + reservationService.addReservation(reservationRequest, memberId) + } returns reservationEntity - @Autowired - private ReservationWithPaymentService reservationWithPaymentService; - @Autowired - private ReservationRepository reservationRepository; - @Autowired - private MemberRepository memberRepository; - @Autowired - private ReservationTimeRepository reservationTimeRepository; - @Autowired - private ThemeRepository themeRepository; - @Autowired - private PaymentRepository paymentRepository; - @Autowired - private CanceledPaymentRepository canceledPaymentRepository; + every { + paymentService.savePayment(paymentApproveResponse, reservationEntity) + } returns paymentEntity.toReservationPaymentResponse() - @Test - @DisplayName("예약과 결제 정보를 추가한다.") - void addReservationWithPayment() { - // given - PaymentApprove.Response paymentInfo = new PaymentApprove.Response("payment-key", "order-id", - OffsetDateTime.now(), 10000L); - LocalDateTime localDateTime = LocalDateTime.now().plusDays(1L).withNano(0); - 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")); - ReservationRequest reservationRequest = new ReservationRequest(date, time.getId(), theme.getId(), "payment-key", - "order-id", 10000L, "NORMAL"); + val result: ReservationResponse = reservationWithPaymentService.addReservationWithPayment( + request = reservationRequest, + paymentInfo = paymentApproveResponse, + memberId = memberId + ) - // when - ReservationResponse reservationResponse = reservationWithPaymentService.addReservationWithPayment( - reservationRequest, paymentInfo, member.getId()); + assertSoftly(result) { + this.id shouldBe reservationEntity.id + this.date shouldBe reservationEntity.date + this.member.id shouldBe reservationEntity.member.id + this.time.id shouldBe reservationEntity.reservationTime.id + this.theme.id shouldBe reservationEntity.theme.id + this.status shouldBe ReservationStatus.CONFIRMED + } + } - // then - reservationRepository.findById(reservationResponse.id) - .ifPresent(reservation -> { - assertThat(reservation.getMember().getId()).isEqualTo(member.getId()); - assertThat(reservation.getTheme().getId()).isEqualTo(theme.getId()); - assertThat(reservation.getDate()).isEqualTo(date); - assertThat(reservation.getReservationTime().getId()).isEqualTo(time.getId()); - assertThat(reservation.getReservationStatus()).isEqualTo(ReservationStatus.CONFIRMED); - }); - PaymentEntity payment = paymentRepository.findByPaymentKey("payment-key"); - assertThat(payment).isNotNull(); - assertThat(payment.getReservation().getId()).isEqualTo(reservationResponse.id); - assertThat(payment.getPaymentKey()).isEqualTo("payment-key"); - assertThat(payment.getOrderId()).isEqualTo("order-id"); - assertThat(payment.getTotalAmount()).isEqualTo(10000L); - } + context("removeReservationWithPayment") { + test("예약 및 결제 정보를 삭제하고, 결제 취소 정보를 저장한다.") { + val paymentCancelRequest: PaymentCancel.Request = PaymentFixture.createCancelRequest().copy( + paymentKey = paymentEntity.paymentKey, + amount = paymentEntity.totalAmount, + cancelReason = "고객 요청" + ) - @Test - @DisplayName("예약 ID를 이용하여 예약과 결제 정보를 제거하고, 결제 취소 정보를 저장한다.") - void removeReservationWithPayment() { - // given - PaymentApprove.Response paymentInfo = new PaymentApprove.Response("payment-key", "order-id", - OffsetDateTime.now(), 10000L); - LocalDateTime localDateTime = LocalDateTime.now().plusDays(1L).withNano(0); - LocalDate date = localDateTime.toLocalDate(); - ReservationTimeEntity time = reservationTimeRepository.save( - new ReservationTimeEntity(null, localDateTime.toLocalTime())); - MemberEntity member = memberRepository.save( - new MemberEntity(null, "member", "admin@email.com", "password", Role.ADMIN)); - ThemeEntity theme = themeRepository.save(new ThemeEntity(null, "name", "desc", "thumbnail")); - ReservationRequest reservationRequest = new ReservationRequest(date, time.getId(), theme.getId(), "payment-key", - "order-id", 10000L, "NORMAL"); + every { + paymentService.cancelPaymentByAdmin(reservationEntity.id!!) + } returns paymentCancelRequest - ReservationResponse reservationResponse = reservationWithPaymentService.addReservationWithPayment( - reservationRequest, paymentInfo, member.getId()); + every { + reservationService.removeReservationById(reservationEntity.id!!, reservationEntity.member.id!!) + } just Runs - // when - PaymentCancel.Request paymentCancelRequest = reservationWithPaymentService.removeReservationWithPayment( - reservationResponse.id, member.getId()); + val result: PaymentCancel.Request = reservationWithPaymentService.removeReservationWithPayment( + reservationId = reservationEntity.id!!, + memberId = reservationEntity.member.id!! + ) - // then - assertThat(paymentCancelRequest.cancelReason).isEqualTo("고객 요청"); - assertThat(reservationRepository.findById(reservationResponse.id)).isEmpty(); - assertThat(paymentRepository.findByPaymentKey("payment-key")).isNull(); - assertThat(canceledPaymentRepository.findByPaymentKey("payment-key")).isNotNull(); - } + result shouldBe paymentCancelRequest + } + } - @Test - @DisplayName("결제 정보가 없으면 True를 반환한다.") - void isNotPaidReservation() { - // 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", "admin@email.com", "password", Role.ADMIN)); - ThemeEntity theme = themeRepository.save(new ThemeEntity(null, "name", "desc", "thumbnail")); + context("isNotPaidReservation") { + test("결제된 예약이면 true를 반환한다.") { + every { + paymentService.isReservationPaid(reservationEntity.id!!) + } returns false - ReservationEntity saved = reservationRepository.save( - new ReservationEntity(null, date, time, theme, member, ReservationStatus.CONFIRMED_PAYMENT_REQUIRED)); + val result: Boolean = reservationWithPaymentService.isNotPaidReservation(reservationEntity.id!!) - // when - boolean result = reservationWithPaymentService.isNotPaidReservation(saved.getId()); + result shouldBe true + } - // then - assertThat(result).isTrue(); - } + test("결제되지 않은 예약이면 false를 반환한다.") { + every { + paymentService.isReservationPaid(reservationEntity.id!!) + } returns true - @Test - @DisplayName("결제 정보가 있으면 False를 반환한다.") - void isPaidReservation() { - // given - PaymentApprove.Response paymentInfo = new PaymentApprove.Response("payment-key", "order-id", - OffsetDateTime.now(), 10000L); - LocalDateTime localDateTime = LocalDateTime.now().plusDays(1L).withNano(0); - LocalDate date = localDateTime.toLocalDate(); - ReservationTimeEntity time = reservationTimeRepository.save( - new ReservationTimeEntity(null, localDateTime.toLocalTime())); - MemberEntity member = memberRepository.save( - new MemberEntity(null, "member", "admin@email.com", "password", Role.ADMIN)); - ThemeEntity theme = themeRepository.save(new ThemeEntity(null, "name", "desc", "thumbnail")); - ReservationRequest reservationRequest = new ReservationRequest(date, time.getId(), theme.getId(), "payment-key", - "order-id", 10000L, "NORMAL"); + val result: Boolean = reservationWithPaymentService.isNotPaidReservation(reservationEntity.id!!) - ReservationResponse reservationResponse = reservationWithPaymentService.addReservationWithPayment( - reservationRequest, paymentInfo, member.getId()); - - // when - boolean result = reservationWithPaymentService.isNotPaidReservation(reservationResponse.id); - - // then - assertThat(result).isFalse(); - } -} + result shouldBe false + } + } + } +}) diff --git a/src/test/java/roomescape/reservation/infrastructure/persistence/ReservationRepositoryTest.kt b/src/test/java/roomescape/reservation/infrastructure/persistence/ReservationRepositoryTest.kt index bfc85aba..40a40923 100644 --- a/src/test/java/roomescape/reservation/infrastructure/persistence/ReservationRepositoryTest.kt +++ b/src/test/java/roomescape/reservation/infrastructure/persistence/ReservationRepositoryTest.kt @@ -161,7 +161,7 @@ class ReservationRepositoryTest( test("결제 정보를 포함한 회원의 예약 목록을 반환한다.") { val payment: PaymentEntity = PaymentFixture.create( - reservationId = reservation.id!! + reservation = reservation ).also { entityManager.persist(it) entityManager.flush() diff --git a/src/test/java/roomescape/util/Fixtures.kt b/src/test/java/roomescape/util/Fixtures.kt index d1009093..99bb0ec4 100644 --- a/src/test/java/roomescape/util/Fixtures.kt +++ b/src/test/java/roomescape/util/Fixtures.kt @@ -128,14 +128,14 @@ object PaymentFixture { orderId: String = ORDER_ID, paymentKey: String = PAYMENT_KEY, totalAmount: Long = AMOUNT, - reservationId: Long = Random.nextLong(), + reservation: ReservationEntity = ReservationFixture.create(id = 1L), approvedAt: OffsetDateTime = OffsetDateTime.now() ): PaymentEntity = PaymentEntity( id = id, orderId = orderId, paymentKey = paymentKey, totalAmount = totalAmount, - reservation = ReservationFixture.create(id = reservationId), + reservation = reservation, approvedAt = approvedAt )