fix(live-room): 진행중 목록 성인 노출 정책과 JP 강제 매핑 검증을 정리한다

This commit is contained in:
2026-03-28 22:53:44 +09:00
parent 0efdfbeed8
commit 2160e7b9dd
6 changed files with 327 additions and 5 deletions

View File

@@ -0,0 +1,265 @@
package kr.co.vividnext.sodalive.live.room
import com.fasterxml.jackson.databind.ObjectMapper
import kr.co.vividnext.sodalive.agora.RtcTokenBuilder
import kr.co.vividnext.sodalive.agora.RtmTokenBuilder
import kr.co.vividnext.sodalive.aws.s3.S3Uploader
import kr.co.vividnext.sodalive.can.CanRepository
import kr.co.vividnext.sodalive.can.charge.ChargeRepository
import kr.co.vividnext.sodalive.can.payment.CanPaymentService
import kr.co.vividnext.sodalive.can.use.UseCanCalculateRepository
import kr.co.vividnext.sodalive.content.ContentType
import kr.co.vividnext.sodalive.explorer.ExplorerQueryRepository
import kr.co.vividnext.sodalive.explorer.profile.CreatorDonationRankingService
import kr.co.vividnext.sodalive.fcm.PushTokenRepository
import kr.co.vividnext.sodalive.i18n.LangContext
import kr.co.vividnext.sodalive.i18n.SodaMessageSource
import kr.co.vividnext.sodalive.live.reservation.LiveReservationRepository
import kr.co.vividnext.sodalive.live.room.cancel.LiveRoomCancelRepository
import kr.co.vividnext.sodalive.live.room.info.LiveRoomInfoRedisRepository
import kr.co.vividnext.sodalive.live.room.kickout.LiveRoomKickOutService
import kr.co.vividnext.sodalive.live.room.menu.LiveRoomMenuService
import kr.co.vividnext.sodalive.live.room.visit.LiveRoomVisitService
import kr.co.vividnext.sodalive.live.roulette.NewRouletteRepository
import kr.co.vividnext.sodalive.live.signature.SignatureCanRepository
import kr.co.vividnext.sodalive.live.tag.LiveTagRepository
import kr.co.vividnext.sodalive.member.Gender
import kr.co.vividnext.sodalive.member.Member
import kr.co.vividnext.sodalive.member.MemberRepository
import kr.co.vividnext.sodalive.member.block.BlockMemberRepository
import kr.co.vividnext.sodalive.member.contentpreference.MemberContentPreferenceService
import kr.co.vividnext.sodalive.member.contentpreference.ViewerContentPreference
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.mockito.Mockito
import org.springframework.context.ApplicationEventPublisher
import org.springframework.data.domain.PageRequest
class LiveRoomServiceAdultVisibilityPolicyTest {
private lateinit var menuService: LiveRoomMenuService
private lateinit var messageSource: SodaMessageSource
private lateinit var langContext: LangContext
private lateinit var repository: LiveRoomRepository
private lateinit var rouletteRepository: NewRouletteRepository
private lateinit var roomInfoRepository: LiveRoomInfoRedisRepository
private lateinit var roomCancelRepository: LiveRoomCancelRepository
private lateinit var kickOutService: LiveRoomKickOutService
private lateinit var blockMemberRepository: BlockMemberRepository
private lateinit var signatureCanRepository: SignatureCanRepository
private lateinit var applicationEventPublisher: ApplicationEventPublisher
private lateinit var useCanCalculateRepository: UseCanCalculateRepository
private lateinit var reservationRepository: LiveReservationRepository
private lateinit var explorerQueryRepository: ExplorerQueryRepository
private lateinit var creatorDonationRankingService: CreatorDonationRankingService
private lateinit var roomVisitService: LiveRoomVisitService
private lateinit var canPaymentService: CanPaymentService
private lateinit var chargeRepository: ChargeRepository
private lateinit var pushTokenRepository: PushTokenRepository
private lateinit var memberRepository: MemberRepository
private lateinit var tagRepository: LiveTagRepository
private lateinit var memberContentPreferenceService: MemberContentPreferenceService
private lateinit var canRepository: CanRepository
private lateinit var objectMapper: ObjectMapper
private lateinit var s3Uploader: S3Uploader
private lateinit var rtcTokenBuilder: RtcTokenBuilder
private lateinit var rtmTokenBuilder: RtmTokenBuilder
private lateinit var service: LiveRoomService
@BeforeEach
fun setup() {
menuService = mock()
messageSource = mock()
langContext = LangContext()
repository = mock()
rouletteRepository = mock()
roomInfoRepository = mock()
roomCancelRepository = mock()
kickOutService = mock()
blockMemberRepository = mock()
signatureCanRepository = mock()
applicationEventPublisher = mock()
useCanCalculateRepository = mock()
reservationRepository = mock()
explorerQueryRepository = mock()
creatorDonationRankingService = mock()
roomVisitService = mock()
canPaymentService = mock()
chargeRepository = mock()
pushTokenRepository = mock()
memberRepository = mock()
tagRepository = mock()
memberContentPreferenceService = mock()
canRepository = mock()
objectMapper = mock()
s3Uploader = mock()
rtcTokenBuilder = mock()
rtmTokenBuilder = mock()
service = LiveRoomService(
menuService = menuService,
messageSource = messageSource,
langContext = langContext,
repository = repository,
rouletteRepository = rouletteRepository,
roomInfoRepository = roomInfoRepository,
roomCancelRepository = roomCancelRepository,
kickOutService = kickOutService,
blockMemberRepository = blockMemberRepository,
signatureCanRepository = signatureCanRepository,
applicationEventPublisher = applicationEventPublisher,
useCanCalculateRepository = useCanCalculateRepository,
reservationRepository = reservationRepository,
explorerQueryRepository = explorerQueryRepository,
creatorDonationRankingService = creatorDonationRankingService,
roomVisitService = roomVisitService,
canPaymentService = canPaymentService,
chargeRepository = chargeRepository,
pushTokenRepository = pushTokenRepository,
memberRepository = memberRepository,
tagRepository = tagRepository,
memberContentPreferenceService = memberContentPreferenceService,
canRepository = canRepository,
objectMapper = objectMapper,
s3Uploader = s3Uploader,
rtcTokenBuilder = rtcTokenBuilder,
rtmTokenBuilder = rtmTokenBuilder,
agoraAppId = "test-agora-app-id",
agoraAppCertificate = "test-agora-app-certificate",
coverImageBucket = "test-cover-image-bucket",
cloudFrontHost = "https://test-cloudfront-host"
)
Mockito.`when`(pushTokenRepository.findByMemberIds(listOf())).thenReturn(listOf())
}
@Test
@DisplayName("NOW 목록 조회는 사용자 성인 설정이 false여도 성인 방 필터를 적용하지 않는다")
fun shouldBypassAdultPreferenceForNowRooms() {
val member = createMember(id = 100L)
Mockito.`when`(memberContentPreferenceService.resolveForQuery(member)).thenReturn(createPreference(isAdult = false))
Mockito.`when`(
repository.getLiveRoomListNow(
offset = 0L,
limit = 20L,
timezone = "Asia/Seoul",
memberId = 100L,
isCreator = false,
isAdult = true,
effectiveGender = Gender.NONE
)
).thenReturn(emptyList())
val response = service.getRoomList(
dateString = null,
status = LiveRoomStatus.NOW,
pageable = PageRequest.of(0, 20),
member = member,
timezone = "Asia/Seoul"
)
assertEquals(0, response.size)
Mockito.verify(repository).getLiveRoomListNow(
offset = 0L,
limit = 20L,
timezone = "Asia/Seoul",
memberId = 100L,
isCreator = false,
isAdult = true,
effectiveGender = Gender.NONE
)
}
@Test
@DisplayName("NOW 목록 조회는 비로그인 사용자도 성인 방 필터를 우회한다")
fun shouldBypassAdultPreferenceForAnonymousNowRooms() {
Mockito.`when`(
repository.getLiveRoomListNow(
offset = 0L,
limit = 20L,
timezone = "Asia/Seoul",
memberId = null,
isCreator = false,
isAdult = true,
effectiveGender = null
)
).thenReturn(emptyList())
val response = service.getRoomList(
dateString = null,
status = LiveRoomStatus.NOW,
pageable = PageRequest.of(0, 20),
member = null,
timezone = "Asia/Seoul"
)
assertEquals(0, response.size)
Mockito.verify(repository).getLiveRoomListNow(
offset = 0L,
limit = 20L,
timezone = "Asia/Seoul",
memberId = null,
isCreator = false,
isAdult = true,
effectiveGender = null
)
}
@Test
@DisplayName("예약 목록 조회는 기존처럼 사용자 성인 설정값을 유지한다")
fun shouldKeepAdultPreferenceForReservationRooms() {
val member = createMember(id = 200L)
Mockito.`when`(memberContentPreferenceService.resolveForQuery(member)).thenReturn(createPreference(isAdult = false))
Mockito.`when`(
repository.getLiveRoomListReservationWithoutDate(
timezone = "Asia/Seoul",
memberId = 200L,
isCreator = false,
isAdult = false,
effectiveGender = Gender.NONE
)
).thenReturn(emptyList())
val response = service.getRoomList(
dateString = null,
status = LiveRoomStatus.RESERVATION,
pageable = PageRequest.of(0, 20),
member = member,
timezone = "Asia/Seoul"
)
assertEquals(0, response.size)
Mockito.verify(repository).getLiveRoomListReservationWithoutDate(
timezone = "Asia/Seoul",
memberId = 200L,
isCreator = false,
isAdult = false,
effectiveGender = Gender.NONE
)
}
private fun createMember(id: Long): Member {
val member = Member(
email = "member$id@test.com",
password = "password",
nickname = "member$id"
)
member.id = id
return member
}
private fun createPreference(isAdult: Boolean): ViewerContentPreference {
return ViewerContentPreference(
countryCode = "KR",
isAdultContentVisible = isAdult,
contentType = ContentType.ALL,
isAdult = isAdult
)
}
private inline fun <reified T> mock(): T {
return Mockito.mock(T::class.java)
}
}

View File

@@ -29,7 +29,7 @@ class MemberContentPreferenceIntegrationTest @Autowired constructor(
private val entityManager: EntityManager
) {
companion object {
private val FORCED_MEMBER_IDS = setOf(2L, 16L, 17L, 29721L, 32050L, 40850L)
private val FORCED_MEMBER_IDS = setOf(2L, 16L, 17L, 29721L, 32050L, 37543L, 40850L)
}
private lateinit var service: MemberContentPreferenceService
@@ -183,10 +183,15 @@ class MemberContentPreferenceIntegrationTest @Autowired constructor(
fun shouldReturnForcedCountryCodeRegardlessOfHeader() {
countryContext.setCountryCode("US")
val jpMember = Member(email = "jp@test.com", password = "password", nickname = "jp-member").apply { id = 2L }
val krMember = Member(email = "kr@test.com", password = "password", nickname = "kr-member").apply { id = 16L }
val jpMember = Member(email = "jp@test.com", password = "password", nickname = "jp-member")
.apply { id = 2L }
val jpMemberNew = Member(email = "jp-new@test.com", password = "password", nickname = "jp-member-new")
.apply { id = 37543L }
val krMember = Member(email = "kr@test.com", password = "password", nickname = "kr-member")
.apply { id = 16L }
assertEquals("JP", service.resolveCountryCode(jpMember))
assertEquals("JP", service.resolveCountryCode(jpMemberNew))
assertEquals("KR", service.resolveCountryCode(krMember))
}

View File

@@ -32,9 +32,11 @@ class MemberContentPreferencePolicyTest {
setRequestCountry("US")
val forcedJpMember = createMember(id = 2L, countryCode = "KR")
val forcedJpMemberNew = createMember(id = 37543L, countryCode = "KR")
val forcedKrMember = createMember(id = 16L, countryCode = "US")
assertEquals("JP", resolveCountryCodeByPolicy(forcedJpMember))
assertEquals("JP", resolveCountryCodeByPolicy(forcedJpMemberNew))
assertEquals("KR", resolveCountryCodeByPolicy(forcedKrMember))
}