test #449

Merged
klaus merged 29 commits from test into main 2026-09-10 07:58:32 +00:00
3 changed files with 34 additions and 19 deletions
Showing only changes of commit 14394b8263 - Show all commits
@@ -1,6 +1,7 @@
package kr.co.vividnext.sodalive.v2.recommendation.application package kr.co.vividnext.sodalive.v2.recommendation.application
import kr.co.vividnext.sodalive.i18n.Lang import kr.co.vividnext.sodalive.i18n.Lang
import kr.co.vividnext.sodalive.i18n.LangContext
import kr.co.vividnext.sodalive.v2.common.domain.CreatorActivityType import kr.co.vividnext.sodalive.v2.common.domain.CreatorActivityType
import kr.co.vividnext.sodalive.v2.recommendation.domain.RecommendationSnapshotWindowPolicy import kr.co.vividnext.sodalive.v2.recommendation.domain.RecommendationSnapshotWindowPolicy
import kr.co.vividnext.sodalive.v2.recommendation.domain.RecommendedSectionType import kr.co.vividnext.sodalive.v2.recommendation.domain.RecommendedSectionType
@@ -29,7 +30,8 @@ class HomeRecommendationQueryService(
private val aiCharacterSnapshotFallbackService: AiCharacterSnapshotFallbackPort? = null, private val aiCharacterSnapshotFallbackService: AiCharacterSnapshotFallbackPort? = null,
private val cheerCreatorSnapshotFallbackService: CheerCreatorSnapshotFallbackPort? = null, private val cheerCreatorSnapshotFallbackService: CheerCreatorSnapshotFallbackPort? = null,
private val popularCommunitySnapshotFallbackService: PopularCommunitySnapshotFallbackPort? = null, private val popularCommunitySnapshotFallbackService: PopularCommunitySnapshotFallbackPort? = null,
private val clock: Clock = Clock.systemUTC() private val clock: Clock = Clock.systemUTC(),
private val langContext: LangContext
) { ) {
private val snapshotWindowPolicy = RecommendationSnapshotWindowPolicy() private val snapshotWindowPolicy = RecommendationSnapshotWindowPolicy()
@@ -76,7 +78,7 @@ class HomeRecommendationQueryService(
memberId: Long? = null, memberId: Long? = null,
includeAdultContents: Boolean = false includeAdultContents: Boolean = false
): List<HomeFirstAudioContentRecord> { ): List<HomeFirstAudioContentRecord> {
return queryPort.findFirstAudioContents(now, offset, limit, memberId, includeAdultContents) return queryPort.findFirstAudioContents(now, offset, limit, memberId, includeAdultContents, langContext.lang.code)
} }
fun findAiCharacterRecommendations( fun findAiCharacterRecommendations(
@@ -38,7 +38,8 @@ interface HomeRecommendationQueryPort {
offset: Long = 0, offset: Long = 0,
limit: Int, limit: Int,
memberId: Long? = null, memberId: Long? = null,
includeAdultContents: Boolean = false includeAdultContents: Boolean = false,
locale: String
): List<HomeFirstAudioContentRecord> ): List<HomeFirstAudioContentRecord>
fun findAiCharacterSnapshots( fun findAiCharacterSnapshots(
@@ -1,6 +1,7 @@
package kr.co.vividnext.sodalive.v2.recommendation.application package kr.co.vividnext.sodalive.v2.recommendation.application
import kr.co.vividnext.sodalive.i18n.Lang import kr.co.vividnext.sodalive.i18n.Lang
import kr.co.vividnext.sodalive.i18n.LangContext
import kr.co.vividnext.sodalive.v2.common.domain.CreatorActivityType import kr.co.vividnext.sodalive.v2.common.domain.CreatorActivityType
import kr.co.vividnext.sodalive.v2.recommendation.domain.RecommendedSectionType import kr.co.vividnext.sodalive.v2.recommendation.domain.RecommendedSectionType
import kr.co.vividnext.sodalive.v2.recommendation.port.out.HomeAiCharacterRecommendationRecord import kr.co.vividnext.sodalive.v2.recommendation.port.out.HomeAiCharacterRecommendationRecord
@@ -27,7 +28,8 @@ import java.time.ZoneOffset
class HomeRecommendationQueryServiceTest { class HomeRecommendationQueryServiceTest {
private val port = FakeHomeRecommendationQueryPort() private val port = FakeHomeRecommendationQueryPort()
private val snapshotPort = FakeHomeRecommendationSnapshotPort() private val snapshotPort = FakeHomeRecommendationSnapshotPort()
private val service = HomeRecommendationQueryService(port, snapshotPort) private val langContext = LangContext()
private val service = HomeRecommendationQueryService(port, snapshotPort, langContext = langContext)
@Test @Test
@DisplayName("다시듣기 테마 콘텐츠는 AUDIO가 아니라 LIVE_REPLAY 활동으로 분류한다") @DisplayName("다시듣기 테마 콘텐츠는 AUDIO가 아니라 LIVE_REPLAY 활동으로 분류한다")
@@ -152,6 +154,7 @@ class HomeRecommendationQueryServiceTest {
assertEquals(now, port.firstAudioNow) assertEquals(now, port.firstAudioNow)
assertEquals(103L, port.firstAudioMemberId) assertEquals(103L, port.firstAudioMemberId)
assertEquals(10, port.firstAudioLimit) assertEquals(10, port.firstAudioLimit)
assertEquals(Lang.KO.code, port.firstAudioLocale)
assertEquals(port.firstAudioContents, contents) assertEquals(port.firstAudioContents, contents)
} }
@@ -159,7 +162,7 @@ class HomeRecommendationQueryServiceTest {
@DisplayName("AI 캐릭터 추천은 최신 스냅샷 20개를 기준으로 순서를 유지해 상세를 조립한다") @DisplayName("AI 캐릭터 추천은 최신 스냅샷 20개를 기준으로 순서를 유지해 상세를 조립한다")
fun shouldFindAiCharactersFromLatestSnapshotsWithLimitAndDetails() { fun shouldFindAiCharactersFromLatestSnapshotsWithLimitAndDetails() {
val fallback = FakeAiCharacterSnapshotFallbackPort(emptyList()) val fallback = FakeAiCharacterSnapshotFallbackPort(emptyList())
val service = HomeRecommendationQueryService(port, snapshotPort, fallback) val service = HomeRecommendationQueryService(port, snapshotPort, fallback, langContext = langContext)
val oldSnapshotAt = LocalDateTime.of(2026, 5, 28, 23, 59, 59) val oldSnapshotAt = LocalDateTime.of(2026, 5, 28, 23, 59, 59)
val latestSnapshotAt = LocalDateTime.of(2026, 5, 29, 23, 59, 59) val latestSnapshotAt = LocalDateTime.of(2026, 5, 29, 23, 59, 59)
snapshotPort.replaceSnapshots( snapshotPort.replaceSnapshots(
@@ -261,7 +264,7 @@ class HomeRecommendationQueryServiceTest {
val fallback = FakeAiCharacterSnapshotFallbackPort( val fallback = FakeAiCharacterSnapshotFallbackPort(
listOf(snapshot(RecommendedSectionType.AI_CHARACTER, 10L, 10.0, LocalDateTime.of(2026, 7, 9, 14, 59, 59))) listOf(snapshot(RecommendedSectionType.AI_CHARACTER, 10L, 10.0, LocalDateTime.of(2026, 7, 9, 14, 59, 59)))
) )
val service = HomeRecommendationQueryService(port, snapshotPort, fallback) val service = HomeRecommendationQueryService(port, snapshotPort, fallback, langContext = langContext)
port.aiCharacterDetails = listOf( port.aiCharacterDetails = listOf(
HomeAiCharacterRecommendationRecord( HomeAiCharacterRecommendationRecord(
characterId = 10L, characterId = 10L,
@@ -286,7 +289,7 @@ class HomeRecommendationQueryServiceTest {
@DisplayName("AI 캐릭터 추천은 fallback 후에도 스냅샷이 없으면 빈 배열을 반환한다") @DisplayName("AI 캐릭터 추천은 fallback 후에도 스냅샷이 없으면 빈 배열을 반환한다")
fun shouldReturnEmptyAiCharactersWhenFallbackDoesNotCreateSnapshots() { fun shouldReturnEmptyAiCharactersWhenFallbackDoesNotCreateSnapshots() {
val fallback = FakeAiCharacterSnapshotFallbackPort(emptyList()) val fallback = FakeAiCharacterSnapshotFallbackPort(emptyList())
val service = HomeRecommendationQueryService(port, snapshotPort, fallback) val service = HomeRecommendationQueryService(port, snapshotPort, fallback, langContext = langContext)
val characters = service.findAiCharacterRecommendations() val characters = service.findAiCharacterRecommendations()
@@ -302,7 +305,7 @@ class HomeRecommendationQueryServiceTest {
val fallback = FakeAiCharacterSnapshotFallbackPort( val fallback = FakeAiCharacterSnapshotFallbackPort(
listOf(snapshot(RecommendedSectionType.AI_CHARACTER, 99L, 99.0, LocalDateTime.of(2026, 7, 9, 14, 59, 59))) listOf(snapshot(RecommendedSectionType.AI_CHARACTER, 99L, 99.0, LocalDateTime.of(2026, 7, 9, 14, 59, 59)))
) )
val service = HomeRecommendationQueryService(port, snapshotPort, fallback) val service = HomeRecommendationQueryService(port, snapshotPort, fallback, langContext = langContext)
snapshotPort.replaceSnapshots( snapshotPort.replaceSnapshots(
RecommendedSectionType.AI_CHARACTER, RecommendedSectionType.AI_CHARACTER,
LocalDateTime.of(2026, 7, 9, 14, 59, 59), LocalDateTime.of(2026, 7, 9, 14, 59, 59),
@@ -325,7 +328,7 @@ class HomeRecommendationQueryServiceTest {
val fallback = FakeAiCharacterSnapshotFallbackPort( val fallback = FakeAiCharacterSnapshotFallbackPort(
listOf(snapshot(RecommendedSectionType.AI_CHARACTER, 99L, 99.0, LocalDateTime.of(2026, 7, 9, 14, 59, 59))) listOf(snapshot(RecommendedSectionType.AI_CHARACTER, 99L, 99.0, LocalDateTime.of(2026, 7, 9, 14, 59, 59)))
) )
val service = HomeRecommendationQueryService(port, snapshotPort, fallback) val service = HomeRecommendationQueryService(port, snapshotPort, fallback, langContext = langContext)
val snapshotAt = LocalDateTime.of(2026, 7, 9, 14, 59, 59) val snapshotAt = LocalDateTime.of(2026, 7, 9, 14, 59, 59)
snapshotPort.replaceSnapshots( snapshotPort.replaceSnapshots(
RecommendedSectionType.AI_CHARACTER, RecommendedSectionType.AI_CHARACTER,
@@ -352,7 +355,7 @@ class HomeRecommendationQueryServiceTest {
val fallback = FakeAiCharacterSnapshotFallbackPort( val fallback = FakeAiCharacterSnapshotFallbackPort(
listOf(snapshot(RecommendedSectionType.AI_CHARACTER, 99L, 99.0, LocalDateTime.of(2026, 7, 9, 14, 59, 59))) listOf(snapshot(RecommendedSectionType.AI_CHARACTER, 99L, 99.0, LocalDateTime.of(2026, 7, 9, 14, 59, 59)))
) )
val service = HomeRecommendationQueryService(port, snapshotPort, fallback) val service = HomeRecommendationQueryService(port, snapshotPort, fallback, langContext = langContext)
val snapshotAt = LocalDateTime.of(2026, 7, 9, 14, 59, 59) val snapshotAt = LocalDateTime.of(2026, 7, 9, 14, 59, 59)
snapshotPort.replaceSnapshots( snapshotPort.replaceSnapshots(
RecommendedSectionType.AI_CHARACTER, RecommendedSectionType.AI_CHARACTER,
@@ -376,7 +379,8 @@ class HomeRecommendationQueryServiceTest {
port, port,
snapshotPort, snapshotPort,
cheerCreatorSnapshotFallbackService = fallback, cheerCreatorSnapshotFallbackService = fallback,
clock = fixedClock() clock = fixedClock(),
langContext = langContext
) )
val snapshotAt = LocalDateTime.of(2026, 7, 9, 14, 59, 59) val snapshotAt = LocalDateTime.of(2026, 7, 9, 14, 59, 59)
snapshotPort.replaceSnapshots( snapshotPort.replaceSnapshots(
@@ -419,7 +423,8 @@ class HomeRecommendationQueryServiceTest {
port, port,
snapshotPort, snapshotPort,
cheerCreatorSnapshotFallbackService = fallback, cheerCreatorSnapshotFallbackService = fallback,
clock = fixedClock() clock = fixedClock(),
langContext = langContext
) )
port.cheerCreatorDetails = listOf( port.cheerCreatorDetails = listOf(
HomeCheerCreatorRecommendationRecord( HomeCheerCreatorRecommendationRecord(
@@ -448,7 +453,8 @@ class HomeRecommendationQueryServiceTest {
port, port,
snapshotPort, snapshotPort,
cheerCreatorSnapshotFallbackService = fallback, cheerCreatorSnapshotFallbackService = fallback,
clock = fixedClock() clock = fixedClock(),
langContext = langContext
) )
val staleSnapshotAt = LocalDateTime.of(2026, 7, 8, 14, 59, 59) val staleSnapshotAt = LocalDateTime.of(2026, 7, 8, 14, 59, 59)
snapshotPort.replaceSnapshots( snapshotPort.replaceSnapshots(
@@ -483,7 +489,8 @@ class HomeRecommendationQueryServiceTest {
port, port,
snapshotPort, snapshotPort,
cheerCreatorSnapshotFallbackService = fallback, cheerCreatorSnapshotFallbackService = fallback,
clock = fixedClock() clock = fixedClock(),
langContext = langContext
) )
snapshotPort.replaceSnapshots( snapshotPort.replaceSnapshots(
RecommendedSectionType.CHEER_CREATOR, RecommendedSectionType.CHEER_CREATOR,
@@ -502,7 +509,7 @@ class HomeRecommendationQueryServiceTest {
@Test @Test
@DisplayName("인기 커뮤니티 추천은 대상일 스냅샷 10개를 기준으로 크리에이터 중복을 제거하고 상세를 조립한다") @DisplayName("인기 커뮤니티 추천은 대상일 스냅샷 10개를 기준으로 크리에이터 중복을 제거하고 상세를 조립한다")
fun shouldFindPopularCommunitiesFromTargetDaySnapshotsWithLimitDetailsAndCreatorUniqueness() { fun shouldFindPopularCommunitiesFromTargetDaySnapshotsWithLimitDetailsAndCreatorUniqueness() {
val service = HomeRecommendationQueryService(port, snapshotPort, clock = fixedClock()) val service = HomeRecommendationQueryService(port, snapshotPort, clock = fixedClock(), langContext = langContext)
val snapshotAt = LocalDateTime.of(2026, 7, 9, 14, 59, 59) val snapshotAt = LocalDateTime.of(2026, 7, 9, 14, 59, 59)
snapshotPort.replaceSnapshots( snapshotPort.replaceSnapshots(
RecommendedSectionType.POPULAR_COMMUNITY, RecommendedSectionType.POPULAR_COMMUNITY,
@@ -576,7 +583,7 @@ class HomeRecommendationQueryServiceTest {
@Test @Test
@DisplayName("인기 커뮤니티 추천은 상위 스냅샷 중복 제거 후 뒤 후보로 기본 10개를 채운다") @DisplayName("인기 커뮤니티 추천은 상위 스냅샷 중복 제거 후 뒤 후보로 기본 10개를 채운다")
fun shouldBackfillPopularCommunitiesAfterRemovingDuplicateCreators() { fun shouldBackfillPopularCommunitiesAfterRemovingDuplicateCreators() {
val service = HomeRecommendationQueryService(port, snapshotPort, clock = fixedClock()) val service = HomeRecommendationQueryService(port, snapshotPort, clock = fixedClock(), langContext = langContext)
val snapshotAt = LocalDateTime.of(2026, 7, 9, 14, 59, 59) val snapshotAt = LocalDateTime.of(2026, 7, 9, 14, 59, 59)
snapshotPort.replaceSnapshots( snapshotPort.replaceSnapshots(
RecommendedSectionType.POPULAR_COMMUNITY, RecommendedSectionType.POPULAR_COMMUNITY,
@@ -621,7 +628,8 @@ class HomeRecommendationQueryServiceTest {
port, port,
snapshotPort, snapshotPort,
popularCommunitySnapshotFallbackService = fallback, popularCommunitySnapshotFallbackService = fallback,
clock = fixedClock() clock = fixedClock(),
langContext = langContext
) )
val staleSnapshotAt = LocalDateTime.of(2026, 7, 8, 14, 59, 59) val staleSnapshotAt = LocalDateTime.of(2026, 7, 8, 14, 59, 59)
snapshotPort.replaceSnapshots( snapshotPort.replaceSnapshots(
@@ -666,7 +674,8 @@ class HomeRecommendationQueryServiceTest {
port, port,
snapshotPort, snapshotPort,
popularCommunitySnapshotFallbackService = fallback, popularCommunitySnapshotFallbackService = fallback,
clock = fixedClock() clock = fixedClock(),
langContext = langContext
) )
snapshotPort.replaceSnapshots( snapshotPort.replaceSnapshots(
RecommendedSectionType.POPULAR_COMMUNITY, RecommendedSectionType.POPULAR_COMMUNITY,
@@ -991,6 +1000,7 @@ class HomeRecommendationQueryServiceTest {
var firstAudioOffset: Long? = null var firstAudioOffset: Long? = null
var firstAudioMemberId: Long? = null var firstAudioMemberId: Long? = null
var firstAudioIncludeAdultContents: Boolean? = null var firstAudioIncludeAdultContents: Boolean? = null
var firstAudioLocale: String? = null
var aiCharacterDetailIds: List<Long> = emptyList() var aiCharacterDetailIds: List<Long> = emptyList()
val aiCharacterDetailIdRequests = mutableListOf<List<Long>>() val aiCharacterDetailIdRequests = mutableListOf<List<Long>>()
var randomAiCharacterExcludeIds: List<Long>? = null var randomAiCharacterExcludeIds: List<Long>? = null
@@ -1121,13 +1131,15 @@ class HomeRecommendationQueryServiceTest {
offset: Long, offset: Long,
limit: Int, limit: Int,
memberId: Long?, memberId: Long?,
includeAdultContents: Boolean includeAdultContents: Boolean,
locale: String
): List<HomeFirstAudioContentRecord> { ): List<HomeFirstAudioContentRecord> {
firstAudioNow = now firstAudioNow = now
firstAudioOffset = offset firstAudioOffset = offset
firstAudioLimit = limit firstAudioLimit = limit
firstAudioMemberId = memberId firstAudioMemberId = memberId
firstAudioIncludeAdultContents = includeAdultContents firstAudioIncludeAdultContents = includeAdultContents
firstAudioLocale = locale
return firstAudioContents return firstAudioContents
} }