feat(admin-calculate): 관리자 라이브 환불 처리와 정산 응답 식별자를 추가한다

This commit is contained in:
2026-03-16 12:25:50 +09:00
parent 02196eba4c
commit e2cbca1b84
10 changed files with 412 additions and 20 deletions

View File

@@ -0,0 +1,224 @@
package kr.co.vividnext.sodalive.admin.calculate
import kr.co.vividnext.sodalive.can.CanRepository
import kr.co.vividnext.sodalive.can.charge.ChargeRepository
import kr.co.vividnext.sodalive.can.payment.PaymentGateway
import kr.co.vividnext.sodalive.can.use.CanUsage
import kr.co.vividnext.sodalive.can.use.UseCan
import kr.co.vividnext.sodalive.can.use.UseCanCalculate
import kr.co.vividnext.sodalive.can.use.UseCanCalculateRepository
import kr.co.vividnext.sodalive.can.use.UseCanCalculateStatus
import kr.co.vividnext.sodalive.common.SodaException
import kr.co.vividnext.sodalive.i18n.LangContext
import kr.co.vividnext.sodalive.i18n.SodaMessageSource
import kr.co.vividnext.sodalive.live.room.LiveRoom
import kr.co.vividnext.sodalive.live.room.LiveRoomRepository
import kr.co.vividnext.sodalive.member.Member
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertThrows
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
import org.mockito.Mockito.any
import org.mockito.Mockito.atLeastOnce
import org.mockito.Mockito.mock
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when`
import java.util.Optional
class AdminCalculateServiceTest {
private val repository = mock(AdminCalculateQueryRepository::class.java)
private val canRepository = mock(CanRepository::class.java)
private val useCanCalculateRepository = mock(UseCanCalculateRepository::class.java)
private val chargeRepository = mock(ChargeRepository::class.java)
private val liveRoomRepository = mock(LiveRoomRepository::class.java)
private val messageSource = mock(SodaMessageSource::class.java)
private val langContext = mock(LangContext::class.java)
private val adminCalculateService = AdminCalculateService(
repository,
canRepository,
useCanCalculateRepository,
chargeRepository,
liveRoomRepository,
messageSource,
langContext
)
@Test
@DisplayName("라이브 환불 성공 테스트")
fun shouldRefundLiveSuccessfully() {
// given
val roomId = 1L
val canUsageStr = "유료"
val request = AdminLiveRefundRequest(roomId, canUsageStr)
val member = Member(password = "pass", nickname = "nick").apply {
id = 10L
pgRewardCan = 100
}
val room = LiveRoom(
title = "title",
notice = "notice",
beginDateTime = java.time.LocalDateTime.now(),
numberOfPeople = 10,
isAdult = false
).apply { id = roomId }
val useCan = UseCan(
canUsage = CanUsage.LIVE,
can = 10,
rewardCan = 0
).apply {
id = 100L
this.member = member
this.room = room
this.isRefund = false
}
val useCanCalculate = UseCanCalculate(
can = 10,
paymentGateway = PaymentGateway.PG,
status = UseCanCalculateStatus.RECEIVED
).apply {
id = 1000L
this.useCan = useCan
}
`when`(liveRoomRepository.findById(roomId)).thenReturn(Optional.of(room))
`when`(canRepository.findAllByRoomIdAndCanUsageAndIsRefundFalse(roomId, CanUsage.LIVE))
.thenReturn(listOf(useCan))
`when`(useCanCalculateRepository.findByUseCanIdAndStatus(100L))
.thenReturn(listOf(useCanCalculate))
// when
adminCalculateService.refundLive(request)
// then
assertTrue(useCan.isRefund)
verify(chargeRepository, atLeastOnce()).save(any())
}
@Test
@DisplayName("환불 전후 캔 잔액 동일성 검증 테스트")
fun shouldMaintainCanBalanceAfterRefund() {
// given
val roomId = 1L
val canUsageStr = "유료"
val request = AdminLiveRefundRequest(roomId, canUsageStr)
val initialPgRewardCan = 100
val spendCanAmount = 30
val member = Member(password = "pass", nickname = "nick").apply {
id = 10L
pgRewardCan = initialPgRewardCan
}
// 사용 전 캔 수 확인
val beforeUseCanCount = member.pgRewardCan
assertEquals(initialPgRewardCan, beforeUseCanCount)
// 캔 사용 시뮬레이션
member.pgRewardCan -= spendCanAmount
val afterUseCanCount = member.pgRewardCan
assertEquals(initialPgRewardCan - spendCanAmount, afterUseCanCount)
val room = LiveRoom(
title = "title",
notice = "notice",
beginDateTime = java.time.LocalDateTime.now(),
numberOfPeople = 10,
isAdult = false
).apply { id = roomId }
val useCan = UseCan(
canUsage = CanUsage.LIVE,
can = spendCanAmount,
rewardCan = 0
).apply {
id = 100L
this.member = member
this.room = room
this.isRefund = false
}
val useCanCalculate = UseCanCalculate(
can = spendCanAmount,
paymentGateway = PaymentGateway.PG,
status = UseCanCalculateStatus.RECEIVED
).apply {
id = 1000L
this.useCan = useCan
}
`when`(liveRoomRepository.findById(roomId)).thenReturn(Optional.of(room))
`when`(canRepository.findAllByRoomIdAndCanUsageAndIsRefundFalse(roomId, CanUsage.LIVE))
.thenReturn(listOf(useCan))
`when`(useCanCalculateRepository.findByUseCanIdAndStatus(100L))
.thenReturn(listOf(useCanCalculate))
// when (환불 실행)
adminCalculateService.refundLive(request)
// then (환불 후 캔 수 확인)
val afterRefundCanCount = member.pgRewardCan
assertEquals(beforeUseCanCount, afterRefundCanCount, "사용 전 캔 수와 환불 후 캔 수가 동일해야 합니다.")
assertTrue(useCan.isRefund)
}
@Test
@DisplayName("존재하지 않는 라이브 방 환불 요청 시 실패 테스트")
fun shouldFailWhenRoomNotFound() {
// given
val roomId = 999L
val canUsageStr = "유료"
val request = AdminLiveRefundRequest(roomId, canUsageStr)
`when`(liveRoomRepository.findById(roomId)).thenReturn(Optional.empty())
// when & then
val exception = assertThrows(SodaException::class.java) {
adminCalculateService.refundLive(request)
}
assertEquals("live.room.not_found", exception.messageKey)
}
@Test
@DisplayName("잘못된 사용 구분 문자열로 환불 요청 시 실패 테스트")
fun shouldFailWhenInvalidCanUsage() {
// given
val roomId = 1L
val canUsageStr = "잘못된구분"
val request = AdminLiveRefundRequest(roomId, canUsageStr)
val room = LiveRoom(
title = "title",
notice = "notice",
beginDateTime = java.time.LocalDateTime.now(),
numberOfPeople = 10,
isAdult = false
).apply { id = roomId }
`when`(liveRoomRepository.findById(roomId)).thenReturn(Optional.of(room))
// when & then
val exception = assertThrows(SodaException::class.java) {
adminCalculateService.refundLive(request)
}
assertTrue(exception.message!!.contains("Invalid canUsageStr"))
}
@Test
@DisplayName("필수 파라미터 누락 시 환불 요청 실패 테스트")
fun shouldFailWhenRequiredParameterMissing() {
// given
val request = AdminLiveRefundRequest(null, "")
// when & then
val exception = assertThrows(SodaException::class.java) {
adminCalculateService.refundLive(request)
}
assertEquals("common.error.invalid_request", exception.messageKey)
}
}