package roomescape.payment.infrastructure.client import io.kotest.assertions.assertSoftly import io.kotest.assertions.throwables.shouldThrow import io.kotest.core.spec.style.FunSpec import io.kotest.matchers.shouldBe import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.test.autoconfigure.web.client.RestClientTest import org.springframework.http.HttpMethod import org.springframework.http.HttpStatus import org.springframework.http.MediaType import org.springframework.test.web.client.MockRestServiceServer import org.springframework.test.web.client.ResponseActions import org.springframework.test.web.client.match.MockRestRequestMatchers.* import org.springframework.test.web.client.response.MockRestResponseCreators.withStatus import org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess import roomescape.common.exception.ErrorType import roomescape.common.exception.RoomescapeException import roomescape.payment.SampleTossPaymentConst import roomescape.payment.web.PaymentApprove import roomescape.payment.web.PaymentCancel @RestClientTest(TossPaymentClient::class) class TossPaymentClientTest( @Autowired val client: TossPaymentClient, @Autowired val mockServer: MockRestServiceServer ) : FunSpec() { init { context("결제 승인 요청") { fun commonAction(): ResponseActions = mockServer.expect { requestTo("/v1/payments/confirm") }.andExpect { method(HttpMethod.POST) }.andExpect { content().contentType(MediaType.APPLICATION_JSON) }.andExpect { content().json(SampleTossPaymentConst.paymentRequestJson) } test("성공 응답") { commonAction().andRespond { withSuccess() .contentType(MediaType.APPLICATION_JSON) .body(SampleTossPaymentConst.confirmJson) .createResponse(it) } // when val paymentRequest = SampleTossPaymentConst.paymentRequest val paymentResponse: PaymentApprove.Response = client.confirmPayment(paymentRequest) assertSoftly(paymentResponse) { this.paymentKey shouldBe paymentRequest.paymentKey this.orderId shouldBe paymentRequest.orderId this.totalAmount shouldBe paymentRequest.amount } } test("400 에러 발생") { commonAction().andRespond { withStatus(HttpStatus.BAD_REQUEST) .contentType(MediaType.APPLICATION_JSON) .body(SampleTossPaymentConst.tossPaymentErrorJson) .createResponse(it) } // when val paymentRequest = SampleTossPaymentConst.paymentRequest // then val exception = shouldThrow { client.confirmPayment(paymentRequest) } assertSoftly(exception) { this.errorType shouldBe ErrorType.PAYMENT_ERROR this.invalidValue shouldBe "[ErrorCode = ERROR_CODE, ErrorMessage = Error message]" this.httpStatus shouldBe HttpStatus.BAD_REQUEST } } } context("결제 취소 요청") { fun commonAction(): ResponseActions = mockServer.expect { requestTo("/v1/payments/${SampleTossPaymentConst.paymentKey}/cancel") }.andExpect { method(HttpMethod.POST) }.andExpect { content().contentType(MediaType.APPLICATION_JSON) }.andExpect { content().json(SampleTossPaymentConst.cancelRequestJson) } test("성공 응답") { commonAction().andRespond { withSuccess() .contentType(MediaType.APPLICATION_JSON) .body(SampleTossPaymentConst.cancelJson) .createResponse(it) } // when val cancelRequest: PaymentCancel.Request = SampleTossPaymentConst.cancelRequest val cancelResponse: PaymentCancel.Response = client.cancelPayment(cancelRequest) assertSoftly(cancelResponse) { this.cancelStatus shouldBe "DONE" this.cancelReason shouldBe cancelRequest.cancelReason this.cancelAmount shouldBe cancelRequest.amount } } test("500 에러 발생") { commonAction().andRespond { withStatus(HttpStatus.INTERNAL_SERVER_ERROR) .contentType(MediaType.APPLICATION_JSON) .body(SampleTossPaymentConst.tossPaymentErrorJson) .createResponse(it) } // when val cancelRequest: PaymentCancel.Request = SampleTossPaymentConst.cancelRequest // then val exception = shouldThrow { client.cancelPayment(cancelRequest) } assertSoftly(exception) { this.errorType shouldBe ErrorType.PAYMENT_SERVER_ERROR this.invalidValue shouldBe "[ErrorCode = ERROR_CODE, ErrorMessage = Error message]" this.httpStatus shouldBe HttpStatus.INTERNAL_SERVER_ERROR } } } } }