test #439
@@ -131,6 +131,7 @@ class CanCouponService(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
fun useCanCoupon(couponNumber: String, memberId: Long): String {
|
fun useCanCoupon(couponNumber: String, memberId: Long): String {
|
||||||
val member = memberRepository.findByIdOrNull(id = memberId)
|
val member = memberRepository.findByIdOrNull(id = memberId)
|
||||||
?: throw SodaException(messageKey = "common.error.bad_credentials")
|
?: throw SodaException(messageKey = "common.error.bad_credentials")
|
||||||
|
|||||||
@@ -0,0 +1,111 @@
|
|||||||
|
package kr.co.vividnext.sodalive.can.coupon
|
||||||
|
|
||||||
|
import kr.co.vividnext.sodalive.member.Member
|
||||||
|
import kr.co.vividnext.sodalive.member.MemberRepository
|
||||||
|
import kr.co.vividnext.sodalive.member.auth.Auth
|
||||||
|
import kr.co.vividnext.sodalive.support.EmbeddedRedisInitializer
|
||||||
|
import org.junit.jupiter.api.Assertions.assertEquals
|
||||||
|
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.annotation.DirtiesContext
|
||||||
|
import org.springframework.test.context.ContextConfiguration
|
||||||
|
import org.springframework.transaction.annotation.Propagation
|
||||||
|
import org.springframework.transaction.annotation.Transactional
|
||||||
|
import org.springframework.transaction.support.TransactionTemplate
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
import javax.persistence.EntityManager
|
||||||
|
|
||||||
|
@SpringBootTest(
|
||||||
|
properties = [
|
||||||
|
"spring.cache.type=none",
|
||||||
|
"spring.datasource.url=jdbc:h2:mem:can-coupon-service-integration;" +
|
||||||
|
"MODE=MySQL;DATABASE_TO_UPPER=false;NON_KEYWORDS=VALUE;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE"
|
||||||
|
]
|
||||||
|
)
|
||||||
|
@ContextConfiguration(initializers = [EmbeddedRedisInitializer::class])
|
||||||
|
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
|
||||||
|
@Transactional(propagation = Propagation.NOT_SUPPORTED)
|
||||||
|
class CanCouponServiceIntegrationTest @Autowired constructor(
|
||||||
|
private val service: CanCouponService,
|
||||||
|
private val memberRepository: MemberRepository,
|
||||||
|
private val couponNumberRepository: CanCouponNumberRepository,
|
||||||
|
private val transactionTemplate: TransactionTemplate,
|
||||||
|
private val entityManager: EntityManager
|
||||||
|
) {
|
||||||
|
@Test
|
||||||
|
@DisplayName("쿠폰 사용은 OSIV off 환경의 실제 서비스 호출 경로에서 lazy auth 접근까지 완료된다")
|
||||||
|
fun shouldUseCouponThroughTransactionalServiceProxyWhenOpenInViewIsDisabled() {
|
||||||
|
val fixture = createAuthenticatedMemberAndCouponFixture()
|
||||||
|
|
||||||
|
service.useCanCoupon(couponNumber = fixture.couponNumber, memberId = fixture.memberId)
|
||||||
|
|
||||||
|
val result = transactionTemplate.execute {
|
||||||
|
val member = memberRepository.findById(fixture.memberId).orElseThrow()
|
||||||
|
val couponNumber = couponNumberRepository.findByCouponNumber(fixture.couponNumber)!!
|
||||||
|
|
||||||
|
CouponUseResult(
|
||||||
|
rewardCan = member.pgRewardCan,
|
||||||
|
usedMemberId = couponNumber.member!!.id!!
|
||||||
|
)
|
||||||
|
}!!
|
||||||
|
|
||||||
|
assertEquals(300, result.rewardCan)
|
||||||
|
assertEquals(fixture.memberId, result.usedMemberId)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createAuthenticatedMemberAndCouponFixture(): Fixture {
|
||||||
|
return transactionTemplate.execute {
|
||||||
|
val member = Member(
|
||||||
|
email = "can-coupon-osiv@test.com",
|
||||||
|
password = "password",
|
||||||
|
nickname = "can-coupon-osiv"
|
||||||
|
)
|
||||||
|
entityManager.persist(member)
|
||||||
|
|
||||||
|
val auth = Auth(
|
||||||
|
name = "테스트",
|
||||||
|
birth = "19900101",
|
||||||
|
uniqueCi = "can-coupon-osiv-ci",
|
||||||
|
di = "can-coupon-osiv-di",
|
||||||
|
gender = 1
|
||||||
|
)
|
||||||
|
auth.member = member
|
||||||
|
entityManager.persist(auth)
|
||||||
|
|
||||||
|
val coupon = CanCoupon(
|
||||||
|
couponName = "OSIV 회귀 테스트 쿠폰",
|
||||||
|
couponType = CouponType.CAN,
|
||||||
|
can = 300,
|
||||||
|
couponCount = 1,
|
||||||
|
validity = LocalDateTime.now().plusDays(1),
|
||||||
|
isActive = true,
|
||||||
|
isMultipleUse = false
|
||||||
|
)
|
||||||
|
entityManager.persist(coupon)
|
||||||
|
|
||||||
|
val couponNumber = CanCouponNumber("OSIVCOUPON1234")
|
||||||
|
couponNumber.canCoupon = coupon
|
||||||
|
entityManager.persist(couponNumber)
|
||||||
|
|
||||||
|
entityManager.flush()
|
||||||
|
val fixture = Fixture(
|
||||||
|
memberId = member.id!!,
|
||||||
|
couponNumber = couponNumber.couponNumber
|
||||||
|
)
|
||||||
|
entityManager.clear()
|
||||||
|
fixture
|
||||||
|
}!!
|
||||||
|
}
|
||||||
|
|
||||||
|
private data class Fixture(
|
||||||
|
val memberId: Long,
|
||||||
|
val couponNumber: String
|
||||||
|
)
|
||||||
|
|
||||||
|
private data class CouponUseResult(
|
||||||
|
val rewardCan: Int,
|
||||||
|
val usedMemberId: Long
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -19,6 +19,7 @@ import org.mockito.Mockito.never
|
|||||||
import org.mockito.Mockito.verify
|
import org.mockito.Mockito.verify
|
||||||
import org.mockito.Mockito.`when`
|
import org.mockito.Mockito.`when`
|
||||||
import org.springframework.context.ApplicationEventPublisher
|
import org.springframework.context.ApplicationEventPublisher
|
||||||
|
import org.springframework.transaction.annotation.Transactional
|
||||||
import java.util.Optional
|
import java.util.Optional
|
||||||
|
|
||||||
class CanCouponServiceTest {
|
class CanCouponServiceTest {
|
||||||
@@ -110,6 +111,20 @@ class CanCouponServiceTest {
|
|||||||
verify(chargeService, never()).chargeByCoupon("COUPON5678", member)
|
verify(chargeService, never()).chargeByCoupon("COUPON5678", member)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("쿠폰 사용은 OSIV off 환경에서도 lazy 연관을 읽을 수 있도록 트랜잭션 안에서 실행된다")
|
||||||
|
fun shouldRunUseCanCouponInTransaction() {
|
||||||
|
val method = CanCouponService::class.java.getDeclaredMethod(
|
||||||
|
"useCanCoupon",
|
||||||
|
String::class.java,
|
||||||
|
java.lang.Long.TYPE
|
||||||
|
)
|
||||||
|
|
||||||
|
val transactional = method.getAnnotation(Transactional::class.java)
|
||||||
|
|
||||||
|
assertEquals(false, transactional?.readOnly)
|
||||||
|
}
|
||||||
|
|
||||||
private fun createMember(memberId: Long): Member {
|
private fun createMember(memberId: Long): Member {
|
||||||
return Member(
|
return Member(
|
||||||
email = "member$memberId@test.com",
|
email = "member$memberId@test.com",
|
||||||
|
|||||||
Reference in New Issue
Block a user