generated from pricelees/issue-pr-template
[#50] Tosspay API Mocking 서버 구현 #51
@ -0,0 +1,188 @@
|
|||||||
|
package com.sangdol.tosspaymock
|
||||||
|
|
||||||
|
import com.sangdol.common.persistence.IDGenerator
|
||||||
|
import com.sangdol.tosspaymock.exception.code.TosspayCancelErrorCode
|
||||||
|
import com.sangdol.tosspaymock.exception.code.TosspayConfirmErrorCode
|
||||||
|
import com.sangdol.tosspaymock.infrastructure.persistence.OrderAmountEntity
|
||||||
|
import com.sangdol.tosspaymock.infrastructure.persistence.OrderAmountRepository
|
||||||
|
import com.sangdol.tosspaymock.web.dto.PaymentCancelRequest
|
||||||
|
import com.sangdol.tosspaymock.web.dto.PaymentConfirmRequest
|
||||||
|
import io.kotest.core.spec.style.FunSpec
|
||||||
|
import io.kotest.matchers.nulls.shouldNotBeNull
|
||||||
|
import io.kotest.matchers.shouldBe
|
||||||
|
import io.restassured.RestAssured
|
||||||
|
import io.restassured.module.kotlin.extensions.Extract
|
||||||
|
import io.restassured.module.kotlin.extensions.Given
|
||||||
|
import io.restassured.module.kotlin.extensions.Then
|
||||||
|
import io.restassured.module.kotlin.extensions.When
|
||||||
|
import org.hamcrest.CoreMatchers
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest
|
||||||
|
import org.springframework.boot.test.web.server.LocalServerPort
|
||||||
|
import org.springframework.http.HttpStatus
|
||||||
|
import org.springframework.http.MediaType
|
||||||
|
|
||||||
|
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
||||||
|
class TosspayApiTest(
|
||||||
|
@LocalServerPort private val port: Int,
|
||||||
|
private val orderAmountRepository: OrderAmountRepository,
|
||||||
|
private val idGenerator: IDGenerator
|
||||||
|
) : FunSpec({
|
||||||
|
|
||||||
|
beforeSpec {
|
||||||
|
RestAssured.port = port
|
||||||
|
}
|
||||||
|
|
||||||
|
afterTest {
|
||||||
|
orderAmountRepository.deleteAll()
|
||||||
|
}
|
||||||
|
|
||||||
|
val authorizationKey = "dGVzdF9nc2tfZG9jc19PYVB6OEw1S2RtUVhrelJ6M3k0N0JNdzY6"
|
||||||
|
|
||||||
|
val paymentConfirmRequest = PaymentConfirmRequest(
|
||||||
|
paymentKey = "5EnNZRJGvaBX7zk2yd8ydw26XvwXkLrx9POLqKQjmAw4b0e1",
|
||||||
|
orderId = "MC4wODU4ODQwMzg4NDk0",
|
||||||
|
amount = 100_000,
|
||||||
|
)
|
||||||
|
|
||||||
|
val paymentCancelRequest = PaymentCancelRequest(
|
||||||
|
cancelAmount = paymentConfirmRequest.amount,
|
||||||
|
cancelReason = "그냥.."
|
||||||
|
)
|
||||||
|
|
||||||
|
context("결제 승인") {
|
||||||
|
val paymentConfirmRequest = PaymentConfirmRequest(
|
||||||
|
paymentKey = "5EnNZRJGvaBX7zk2yd8ydw26XvwXkLrx9POLqKQjmAw4b0e1",
|
||||||
|
orderId = "MC4wODU4ODQwMzg4NDk0",
|
||||||
|
amount = 100_000,
|
||||||
|
)
|
||||||
|
|
||||||
|
test("Basic Authorization 헤더가 없으면 실패한다.") {
|
||||||
|
Given {
|
||||||
|
contentType(MediaType.APPLICATION_JSON_VALUE)
|
||||||
|
body(paymentConfirmRequest)
|
||||||
|
} When {
|
||||||
|
post("/v1/payments/confirm")
|
||||||
|
} Then {
|
||||||
|
statusCode(HttpStatus.BAD_REQUEST.value())
|
||||||
|
body("code", CoreMatchers.equalTo(TosspayConfirmErrorCode.INVALID_API_KEY.name))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
test("Basic Authorization 헤더가 Base64 형식이 아니면 실패한다.") {
|
||||||
|
Given {
|
||||||
|
contentType(MediaType.APPLICATION_JSON_VALUE)
|
||||||
|
header("Authorization", "Basic hello-world")
|
||||||
|
body(paymentConfirmRequest)
|
||||||
|
} When {
|
||||||
|
post("/v1/payments/confirm")
|
||||||
|
} Then {
|
||||||
|
statusCode(HttpStatus.UNAUTHORIZED.value())
|
||||||
|
body("code", CoreMatchers.equalTo(TosspayConfirmErrorCode.UNAUTHORIZED_KEY.name))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
test("임의의 결제 정보를 반환하며, 금액은 별도로 저장한다.") {
|
||||||
|
val paymentKey = Given {
|
||||||
|
contentType(MediaType.APPLICATION_JSON_VALUE)
|
||||||
|
header("Authorization", "Basic $authorizationKey")
|
||||||
|
body(paymentConfirmRequest)
|
||||||
|
} When {
|
||||||
|
post("/v1/payments/confirm")
|
||||||
|
} Then {
|
||||||
|
statusCode(HttpStatus.OK.value())
|
||||||
|
} Extract {
|
||||||
|
path<String>("paymentKey")
|
||||||
|
}
|
||||||
|
|
||||||
|
paymentKey shouldBe paymentConfirmRequest.paymentKey
|
||||||
|
orderAmountRepository.findByPaymentKey(paymentKey).shouldNotBeNull()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
context("결제 취소") {
|
||||||
|
test("Basic Authorization 헤더가 없으면 실패한다.") {
|
||||||
|
Given {
|
||||||
|
contentType(MediaType.APPLICATION_JSON_VALUE)
|
||||||
|
body(paymentCancelRequest)
|
||||||
|
} When {
|
||||||
|
post("/v1/payments/${paymentConfirmRequest.paymentKey}/cancel")
|
||||||
|
} Then {
|
||||||
|
statusCode(HttpStatus.BAD_REQUEST.value())
|
||||||
|
body("code", CoreMatchers.equalTo(TosspayConfirmErrorCode.INVALID_API_KEY.name))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
test("Basic Authorization 헤더가 Base64 형식이 아니면 실패한다.") {
|
||||||
|
Given {
|
||||||
|
contentType(MediaType.APPLICATION_JSON_VALUE)
|
||||||
|
header("Authorization", "Basic hello-world")
|
||||||
|
body(paymentCancelRequest)
|
||||||
|
} When {
|
||||||
|
post("/v1/payments/${paymentConfirmRequest.paymentKey}/cancel")
|
||||||
|
} Then {
|
||||||
|
statusCode(HttpStatus.UNAUTHORIZED.value())
|
||||||
|
body("code", CoreMatchers.equalTo(TosspayConfirmErrorCode.UNAUTHORIZED_KEY.name))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
context("정상 응답") {
|
||||||
|
lateinit var orderAmount: OrderAmountEntity
|
||||||
|
|
||||||
|
beforeTest {
|
||||||
|
orderAmount = OrderAmountEntity(
|
||||||
|
id = idGenerator.create(),
|
||||||
|
paymentKey = paymentConfirmRequest.paymentKey,
|
||||||
|
approvedAmount = (paymentConfirmRequest.amount - 1000),
|
||||||
|
easypayDiscountAmount = 1000,
|
||||||
|
cardDiscountAmount = 0,
|
||||||
|
transferDiscountAmount = 0
|
||||||
|
).also {
|
||||||
|
orderAmountRepository.saveAndFlush(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
test("요청에 cancelAmount를 포함하지 않으면 전체 금액을 취소한다.") {
|
||||||
|
Given {
|
||||||
|
contentType(MediaType.APPLICATION_JSON_VALUE)
|
||||||
|
header("Authorization", "Basic $authorizationKey")
|
||||||
|
body(PaymentCancelRequest(cancelReason = "그냥!"))
|
||||||
|
} When {
|
||||||
|
post("/v1/payments/${paymentConfirmRequest.paymentKey}/cancel")
|
||||||
|
} Then {
|
||||||
|
statusCode(HttpStatus.OK.value())
|
||||||
|
body("paymentKey", CoreMatchers.equalTo(paymentConfirmRequest.paymentKey))
|
||||||
|
body("cancels.easyPayDiscountAmount", CoreMatchers.equalTo(orderAmount.easypayDiscountAmount))
|
||||||
|
body("cancels.cancelAmount", CoreMatchers.equalTo(orderAmount.totalAmount()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
test("이전 결제 정보의 할인 금액을 가져온 뒤 반환한다.") {
|
||||||
|
Given {
|
||||||
|
contentType(MediaType.APPLICATION_JSON_VALUE)
|
||||||
|
header("Authorization", "Basic $authorizationKey")
|
||||||
|
body(paymentCancelRequest)
|
||||||
|
} When {
|
||||||
|
post("/v1/payments/${paymentConfirmRequest.paymentKey}/cancel")
|
||||||
|
} Then {
|
||||||
|
statusCode(HttpStatus.OK.value())
|
||||||
|
body("paymentKey", CoreMatchers.equalTo(paymentConfirmRequest.paymentKey))
|
||||||
|
body("cancels.easyPayDiscountAmount", CoreMatchers.equalTo(orderAmount.easypayDiscountAmount))
|
||||||
|
body("cancels.cancelAmount", CoreMatchers.equalTo(paymentCancelRequest.cancelAmount))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
test("이전 결제 정보가 없으면 실패한다.") {
|
||||||
|
Given {
|
||||||
|
contentType(MediaType.APPLICATION_JSON_VALUE)
|
||||||
|
header("Authorization", "Basic $authorizationKey")
|
||||||
|
body(paymentCancelRequest)
|
||||||
|
} When {
|
||||||
|
post("/v1/payments/notExistPaymentKey/cancel")
|
||||||
|
} Then {
|
||||||
|
statusCode(HttpStatus.NOT_FOUND.value())
|
||||||
|
body("code", CoreMatchers.equalTo(TosspayCancelErrorCode.NOT_FOUND_PAYMENT.name))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
Loading…
x
Reference in New Issue
Block a user