fix(live): 라이브 예약 트랜잭션 경계를 보강한다

This commit is contained in:
2026-07-15 18:56:49 +09:00
parent 28dcc4e907
commit 7167e6b3de
3 changed files with 147 additions and 6 deletions

View File

@@ -29,6 +29,7 @@ class LiveReservationService(
@Value("\${cloud.aws.cloud-front.host}")
private val cloudFrontHost: String
) {
@Transactional
fun makeReservation(request: MakeLiveReservationRequest, memberId: Long): MakeLiveReservationResponse {
val room = liveRoomRepository.findByIdOrNull(id = request.roomId)
?: throw SodaException(messageKey = "live.reservation.invalid_request_retry")

View File

@@ -0,0 +1,121 @@
package kr.co.vividnext.sodalive.live.reservation
import kr.co.vividnext.sodalive.live.room.LiveRoom
import kr.co.vividnext.sodalive.member.Member
import kr.co.vividnext.sodalive.support.EmbeddedRedisInitializer
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.BeforeEach
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.mock.web.MockHttpServletRequest
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 org.springframework.web.context.request.RequestContextHolder
import org.springframework.web.context.request.ServletRequestAttributes
import java.time.LocalDateTime
import javax.persistence.EntityManager
@SpringBootTest(
properties = [
"spring.cache.type=none",
"spring.datasource.url=jdbc:h2:mem:live-reservation-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 LiveReservationServiceIntegrationTest @Autowired constructor(
private val service: LiveReservationService,
private val repository: LiveReservationRepository,
private val transactionTemplate: TransactionTemplate,
private val entityManager: EntityManager
) {
@BeforeEach
fun setUpRequestContext() {
RequestContextHolder.setRequestAttributes(ServletRequestAttributes(MockHttpServletRequest()))
}
@AfterEach
fun resetRequestContext() {
RequestContextHolder.resetRequestAttributes()
}
@Test
@DisplayName("라이브 예약은 OSIV off 환경에서도 lazy reservations 접근까지 완료된다")
fun shouldMakeReservationThroughTransactionalServiceProxyWhenOpenInViewIsDisabled() {
val fixture = createFixture()
val response = service.makeReservation(
request = MakeLiveReservationRequest(
roomId = fixture.roomId,
container = "web",
timezone = "Asia/Seoul"
),
memberId = fixture.memberId
)
val savedReservation = transactionTemplate.execute {
val reservation = repository.findById(response.reservationId).orElseThrow()
SavedReservation(
roomId = reservation.room!!.id!!,
memberId = reservation.member!!.id!!
)
}!!
assertEquals(fixture.roomId, savedReservation.roomId)
assertEquals(fixture.memberId, savedReservation.memberId)
}
private fun createFixture(): Fixture {
return transactionTemplate.execute {
val creator = Member(
email = "live-reservation-creator@test.com",
password = "password",
nickname = "live-reservation-creator"
)
entityManager.persist(creator)
val member = Member(
email = "live-reservation-member@test.com",
password = "password",
nickname = "live-reservation-member"
)
entityManager.persist(member)
val room = LiveRoom(
title = "예약 라이브",
notice = "예약 라이브 안내",
beginDateTime = LocalDateTime.now().plusDays(1),
numberOfPeople = 10,
isAdult = false
)
room.member = creator
entityManager.persist(room)
entityManager.flush()
val fixture = Fixture(
roomId = room.id!!,
memberId = member.id!!
)
entityManager.clear()
fixture
}!!
}
private data class Fixture(
val roomId: Long,
val memberId: Long
)
private data class SavedReservation(
val roomId: Long,
val memberId: Long
)
}