feat(live-recommend): 추천 크리에이터 배너를 언어별로 등록하고 노출한다

This commit is contained in:
2026-04-02 18:29:57 +09:00
parent 7f1606a8aa
commit a5ce4b6e0a
13 changed files with 256 additions and 24 deletions
@@ -2,6 +2,7 @@ package kr.co.vividnext.sodalive.live.recommend
import com.querydsl.jpa.impl.JPAQueryFactory
import kr.co.vividnext.sodalive.configs.QueryDslConfig
import kr.co.vividnext.sodalive.i18n.Lang
import kr.co.vividnext.sodalive.member.Member
import kr.co.vividnext.sodalive.member.MemberRepository
import kr.co.vividnext.sodalive.member.MemberRole
@@ -10,6 +11,7 @@ import kr.co.vividnext.sodalive.member.block.BlockMemberRepository
import kr.co.vividnext.sodalive.member.following.CreatorFollowing
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.autoconfigure.orm.jpa.DataJpaTest
@@ -34,6 +36,7 @@ class LiveRecommendRepositoryTest @Autowired constructor(
}
@Test
@DisplayName("추천 크리에이터 조회는 차단 관계를 양방향으로 제외한다")
fun shouldExcludeBlockedCreatorsInBothDirections() {
val viewer = saveMember(nickname = "viewer", role = MemberRole.USER)
val creatorBlockedByViewer = saveMember(nickname = "creator-blocked-by-viewer", role = MemberRole.CREATOR)
@@ -50,13 +53,14 @@ class LiveRecommendRepositoryTest @Autowired constructor(
entityManager.flush()
entityManager.clear()
val result = liveRecommendRepository.getRecommendLive(memberId = viewer.id, isAdult = true)
val result = liveRecommendRepository.getRecommendLive(memberId = viewer.id, isAdult = true, lang = Lang.KO)
assertEquals(1, result.size)
assertEquals(creatorAllowed.id, result[0].creatorId)
}
@Test
@DisplayName("추천 크리에이터 조회는 비활성 차단 관계를 제외하지 않는다")
fun shouldKeepCreatorWhenBlockRelationIsInactive() {
val viewer = saveMember(nickname = "viewer-inactive", role = MemberRole.USER)
val creator = saveMember(nickname = "creator-inactive", role = MemberRole.CREATOR)
@@ -67,13 +71,14 @@ class LiveRecommendRepositoryTest @Autowired constructor(
entityManager.flush()
entityManager.clear()
val result = liveRecommendRepository.getRecommendLive(memberId = viewer.id, isAdult = true)
val result = liveRecommendRepository.getRecommendLive(memberId = viewer.id, isAdult = true, lang = Lang.KO)
assertEquals(1, result.size)
assertEquals(creator.id, result[0].creatorId)
}
@Test
@DisplayName("크리에이터 팔로잉 전체 조회는 알림 여부를 포함한다")
fun shouldReturnFollowingCreatorListWithNotifyFlag() {
val viewer = saveMember(nickname = "viewer-following", role = MemberRole.USER)
val creatorA = saveMember(nickname = "creator-following-a", role = MemberRole.CREATOR)
@@ -98,6 +103,25 @@ class LiveRecommendRepositoryTest @Autowired constructor(
assertEquals(false, isNotifyByCreatorId[creatorB.id])
}
@Test
@DisplayName("추천 크리에이터 조회는 요청 언어와 일치하는 배너만 반환한다")
fun shouldReturnOnlyRequestedLanguageBanners() {
val viewer = saveMember(nickname = "viewer-lang", role = MemberRole.USER)
val koreanCreator = saveMember(nickname = "creator-ko", role = MemberRole.CREATOR)
val japaneseCreator = saveMember(nickname = "creator-ja", role = MemberRole.CREATOR)
saveBanner(creator = koreanCreator, order = 1, lang = Lang.KO)
saveBanner(creator = japaneseCreator, order = 2, lang = Lang.JA)
entityManager.flush()
entityManager.clear()
val result = liveRecommendRepository.getRecommendLive(memberId = viewer.id, isAdult = true, lang = Lang.JA)
assertEquals(1, result.size)
assertEquals(japaneseCreator.id, result.first().creatorId)
}
private fun saveMember(nickname: String, role: MemberRole): Member {
return memberRepository.saveAndFlush(
Member(
@@ -110,11 +134,12 @@ class LiveRecommendRepositoryTest @Autowired constructor(
)
}
private fun saveBanner(creator: Member, order: Int) {
private fun saveBanner(creator: Member, order: Int, lang: Lang = Lang.KO) {
val banner = RecommendLiveCreatorBanner(
startDate = LocalDateTime.now().minusDays(1),
endDate = LocalDateTime.now().plusDays(1),
isAdult = false,
lang = lang,
orders = order,
image = "recommend/$order.png"
)
@@ -1,5 +1,6 @@
package kr.co.vividnext.sodalive.live.recommend
import kr.co.vividnext.sodalive.i18n.Lang
import kr.co.vividnext.sodalive.member.Member
import kr.co.vividnext.sodalive.member.auth.Auth
import kr.co.vividnext.sodalive.member.block.BlockMemberRepository
@@ -58,12 +59,12 @@ class LiveRecommendServiceTest {
isAdult = true
)
)
Mockito.`when`(liveRecommendCacheService.getRecommendLive(memberId = member.id, isAdult = true)).thenReturn(expected)
Mockito.`when`(liveRecommendCacheService.getRecommendLive(member.id, true, Lang.JA)).thenReturn(expected)
val result = service.getRecommendLive(member)
val result = service.getRecommendLive(member, Lang.JA)
assertEquals(expected, result)
Mockito.verify(liveRecommendCacheService).getRecommendLive(memberId = member.id, isAdult = true)
Mockito.verify(liveRecommendCacheService).getRecommendLive(member.id, true, Lang.JA)
Mockito.verifyNoInteractions(repository)
Mockito.verifyNoInteractions(blockMemberRepository)
}
@@ -71,12 +72,12 @@ class LiveRecommendServiceTest {
@Test
fun shouldDelegateToRepositoryAsGuestWhenMemberIsNull() {
val expected = listOf(GetRecommendLiveResponse(imageUrl = "https://cdn.test/recommend-guest.png", creatorId = 88L))
Mockito.`when`(liveRecommendCacheService.getRecommendLive(memberId = null, isAdult = false)).thenReturn(expected)
Mockito.`when`(liveRecommendCacheService.getRecommendLive(null, false, Lang.EN)).thenReturn(expected)
val result = service.getRecommendLive(null)
val result = service.getRecommendLive(null, Lang.EN)
assertEquals(expected, result)
Mockito.verify(liveRecommendCacheService).getRecommendLive(memberId = null, isAdult = false)
Mockito.verify(liveRecommendCacheService).getRecommendLive(null, false, Lang.EN)
Mockito.verifyNoInteractions(repository)
Mockito.verifyNoInteractions(blockMemberRepository)
Mockito.verifyNoInteractions(memberContentPreferenceService)