feat(recommend): 홈 장르 추천 후보 조회를 보강한다

This commit is contained in:
2026-06-04 17:22:23 +09:00
parent 410814ef33
commit 81f1bcc4ef
2 changed files with 286 additions and 66 deletions

View File

@@ -1504,8 +1504,95 @@ class DefaultHomeRecommendationQueryRepositoryTest @Autowired constructor(
}
@Test
@DisplayName("장르 기반 크리에이터 추천은 서비스 중복 제거용 후보 그룹을 genreLimit보다 많이 반환한다")
fun shouldReturnMoreCandidateGroupsThanFinalGenreLimitForServiceDeduplicationBackfill() {
@DisplayName("조회 이력이 없으면 크리에이터 8명을 채울 수 있는 콘텐츠 테마 후보를 먼저 반환한다")
fun shouldRecommendFullRandomThemeCandidatesFirstWhenViewHistoryDoesNotExist() {
repeat(5) { themeIndex ->
val theme = saveTheme("no-history-underfilled-theme-$themeIndex")
saveAudioContent(
saveMember("no-history-underfilled-creator-$themeIndex", MemberRole.CREATOR),
LocalDateTime.of(2026, 5, 30, 10, 0).plusMinutes(themeIndex.toLong()),
isActive = true,
theme = theme
)
}
repeat(5) { themeIndex ->
val theme = saveTheme("no-history-full-theme-$themeIndex")
repeat(8) { creatorIndex ->
saveAudioContent(
saveMember("no-history-full-$themeIndex-creator-$creatorIndex", MemberRole.CREATOR),
LocalDateTime.of(2026, 5, 30, 11, 0).plusMinutes((themeIndex * 10 + creatorIndex).toLong()),
isActive = true,
theme = theme
)
}
}
flushAndClear()
val recommendations = repository.findGenreCreatorRecommendations(
memberId = null,
includeAdultGenres = false,
genreLimit = 5,
creatorLimit = 8
)
assertEquals(true, recommendations.size >= 5)
assertEquals(true, recommendations.take(5).all { it.genreName.startsWith("no-history-full-theme-") })
assertEquals(true, recommendations.take(5).all { it.creators.size == 8 })
}
@Test
@DisplayName("조회 이력 콘텐츠 테마는 8명이 되지 않아도 후보에 포함하고 부족한 장르는 8명 보유 테마 후보로 채운다")
fun shouldKeepViewedThemeCandidateEvenWhenUnderfilledAndReturnFullRandomThemeCandidates() {
val viewer = saveMember("viewed-partial-viewer", MemberRole.USER)
val viewedTheme = saveTheme("viewed-partial-theme")
val viewedContent = saveAudioContent(
saveMember("viewed-partial-creator", MemberRole.CREATOR),
LocalDateTime.of(2026, 5, 30, 10, 0),
isActive = true,
theme = viewedTheme
)
repeat(5) { themeIndex ->
val theme = saveTheme("viewed-fill-full-theme-$themeIndex")
repeat(8) { creatorIndex ->
saveAudioContent(
saveMember("viewed-fill-full-$themeIndex-creator-$creatorIndex", MemberRole.CREATOR),
LocalDateTime.of(2026, 5, 30, 11, 0).plusMinutes((themeIndex * 10 + creatorIndex).toLong()),
isActive = true,
theme = theme
)
}
}
entityManager.persist(
CreatorContentViewHistory(
memberId = viewer.id!!,
contentId = viewedContent.id!!,
genreId = 999L,
viewedAt = LocalDateTime.of(2026, 5, 31, 10, 0)
)
)
flushAndClear()
val recommendations = repository.findGenreCreatorRecommendations(
memberId = viewer.id!!,
includeAdultGenres = false,
genreLimit = 5,
creatorLimit = 8
)
assertEquals(true, recommendations.size >= 5)
assertEquals(true, recommendations.any { it.genreId == viewedTheme.id && it.creators.size == 1 })
assertEquals(
true,
recommendations
.filter { it.genreId != viewedTheme.id }
.take(4)
.all { it.genreName.startsWith("viewed-fill-full-theme-") && it.creators.size == 8 }
)
}
@Test
@DisplayName("장르 기반 크리에이터 추천 repository는 service 보충용 후보 그룹을 genreLimit보다 많이 반환한다")
fun shouldReturnMoreCandidateGroupsThanFinalGenreLimitForServiceBackfill() {
val sharedCreator = saveMember("candidate-shared", MemberRole.CREATOR)
val backfillCreator = saveMember("candidate-backfill", MemberRole.CREATOR)
val firstTheme = saveTheme("candidate-first-theme")