test #436
@@ -601,7 +601,7 @@ class DefaultHomeRecommendationQueryRepository(
|
|||||||
join use_can uc on uc.id = ucc.use_can_id
|
join use_can uc on uc.id = ucc.use_can_id
|
||||||
where ucc.status = 'RECEIVED'
|
where ucc.status = 'RECEIVED'
|
||||||
and uc.is_refund = false
|
and uc.is_refund = false
|
||||||
and uc.can_usage = 'CHANNEL_DONATION'
|
and uc.can_usage in ('CHANNEL_DONATION', 'DONATION')
|
||||||
and ucc.created_at >= :windowStart
|
and ucc.created_at >= :windowStart
|
||||||
and ucc.created_at < :windowEndExclusive
|
and ucc.created_at < :windowEndExclusive
|
||||||
group by ucc.recipient_creator_id
|
group by ucc.recipient_creator_id
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ open class RecommendationSnapshotRefreshService(
|
|||||||
@Transactional
|
@Transactional
|
||||||
open fun refreshCheerCreatorSnapshots(nowUtc: LocalDateTime = LocalDateTime.now(ZoneOffset.UTC)): Int {
|
open fun refreshCheerCreatorSnapshots(nowUtc: LocalDateTime = LocalDateTime.now(ZoneOffset.UTC)): Int {
|
||||||
val startedAt = System.currentTimeMillis()
|
val startedAt = System.currentTimeMillis()
|
||||||
val window = windowPolicy.previousKstDayUtcWindow(nowUtc)
|
val window = windowPolicy.previousKstSevenDayUtcWindow(nowUtc)
|
||||||
val snapshots = queryPort.findCheerCreatorSnapshots(
|
val snapshots = queryPort.findCheerCreatorSnapshots(
|
||||||
window.startUtc,
|
window.startUtc,
|
||||||
window.endExclusiveUtc,
|
window.endExclusiveUtc,
|
||||||
|
|||||||
@@ -773,8 +773,8 @@ class DefaultHomeRecommendationQueryRepositoryTest @Autowired constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("최근 응원 스냅샷은 전날 half-open 범위의 CHANNEL_DONATION 금액과 distinct 후원 수만 집계한다")
|
@DisplayName("최근 응원 스냅샷은 half-open 범위의 CHANNEL_DONATION과 DONATION 금액 및 distinct 후원 수만 집계한다")
|
||||||
fun shouldFindCheerCreatorSnapshotsWithChannelDonationOnly() {
|
fun shouldFindCheerCreatorSnapshotsWithChannelAndGeneralDonation() {
|
||||||
val windowStart = LocalDateTime.of(2026, 5, 28, 15, 0)
|
val windowStart = LocalDateTime.of(2026, 5, 28, 15, 0)
|
||||||
val windowEndExclusive = LocalDateTime.of(2026, 5, 29, 15, 0)
|
val windowEndExclusive = LocalDateTime.of(2026, 5, 29, 15, 0)
|
||||||
val snapshotAt = windowEndExclusive.minusSeconds(1)
|
val snapshotAt = windowEndExclusive.minusSeconds(1)
|
||||||
@@ -877,9 +877,9 @@ class DefaultHomeRecommendationQueryRepositoryTest @Autowired constructor(
|
|||||||
val snapshots = repository.findCheerCreatorSnapshots(windowStart, windowEndExclusive, limit = 10)
|
val snapshots = repository.findCheerCreatorSnapshots(windowStart, windowEndExclusive, limit = 10)
|
||||||
|
|
||||||
val expectedScore = scorePolicy.calculateCheerScore(
|
val expectedScore = scorePolicy.calculateCheerScore(
|
||||||
donationAmount = 150,
|
donationAmount = 1_150,
|
||||||
fanTalkCount = 1,
|
fanTalkCount = 1,
|
||||||
donationCount = 1,
|
donationCount = 2,
|
||||||
newBoost = scorePolicy.calculateCheerCreatorNewBoost(LocalDateTime.of(2026, 5, 10, 12, 0), snapshotAt)
|
newBoost = scorePolicy.calculateCheerCreatorNewBoost(LocalDateTime.of(2026, 5, 10, 12, 0), snapshotAt)
|
||||||
)
|
)
|
||||||
assertEquals(1, snapshots.size)
|
assertEquals(1, snapshots.size)
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ class RecommendationSnapshotRefreshServiceTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("일 스냅샷 갱신은 전날 23시 59분 59초 기준으로 DB 점수 계산 스냅샷을 저장한다")
|
@DisplayName("일 스냅샷 갱신은 응원 크리에이터를 인기 커뮤니티와 같은 7일 window로 저장한다")
|
||||||
fun shouldRefreshDailySnapshotsWithPreviousDayEndSnapshotAt(output: CapturedOutput) {
|
fun shouldRefreshDailySnapshotsWithPreviousDayEndSnapshotAt(output: CapturedOutput) {
|
||||||
val snapshotPort = FakeRecommendationSnapshotPort()
|
val snapshotPort = FakeRecommendationSnapshotPort()
|
||||||
val queryPort = Mockito.mock(HomeRecommendationQueryPort::class.java)
|
val queryPort = Mockito.mock(HomeRecommendationQueryPort::class.java)
|
||||||
@@ -71,7 +71,7 @@ class RecommendationSnapshotRefreshServiceTest {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
Mockito.`when`(queryPort.findCheerCreatorSnapshots(aiWindowStart, aiWindowEndExclusive, 16)).thenReturn(
|
Mockito.`when`(queryPort.findCheerCreatorSnapshots(windowStart, windowEndExclusive, 16)).thenReturn(
|
||||||
listOf(
|
listOf(
|
||||||
RecommendationSnapshotRecord(
|
RecommendationSnapshotRecord(
|
||||||
sectionType = RecommendedSectionType.CHEER_CREATOR,
|
sectionType = RecommendedSectionType.CHEER_CREATOR,
|
||||||
@@ -112,7 +112,7 @@ class RecommendationSnapshotRefreshServiceTest {
|
|||||||
assertEquals(40.0, communitySnapshots.single().score, 0.0001)
|
assertEquals(40.0, communitySnapshots.single().score, 0.0001)
|
||||||
|
|
||||||
Mockito.verify(queryPort).findAiCharacterSnapshots(aiWindowStart, aiWindowEndExclusive, 20)
|
Mockito.verify(queryPort).findAiCharacterSnapshots(aiWindowStart, aiWindowEndExclusive, 20)
|
||||||
Mockito.verify(queryPort).findCheerCreatorSnapshots(aiWindowStart, aiWindowEndExclusive, 16)
|
Mockito.verify(queryPort).findCheerCreatorSnapshots(windowStart, windowEndExclusive, 16)
|
||||||
Mockito.verify(queryPort).findPopularCommunitySnapshots(windowStart, windowEndExclusive, 20)
|
Mockito.verify(queryPort).findPopularCommunitySnapshots(windowStart, windowEndExclusive, 20)
|
||||||
assertEquals(true, output.out.contains("event=recommendation_snapshot_refresh_success"))
|
assertEquals(true, output.out.contains("event=recommendation_snapshot_refresh_success"))
|
||||||
assertEquals(true, output.out.contains("aiCharacterCount=1"))
|
assertEquals(true, output.out.contains("aiCharacterCount=1"))
|
||||||
@@ -135,8 +135,8 @@ class RecommendationSnapshotRefreshServiceTest {
|
|||||||
).thenReturn(emptyList())
|
).thenReturn(emptyList())
|
||||||
Mockito.`when`(
|
Mockito.`when`(
|
||||||
queryPort.findCheerCreatorSnapshots(
|
queryPort.findCheerCreatorSnapshots(
|
||||||
LocalDateTime.of(2026, 5, 28, 15, 0, 0),
|
windowStart,
|
||||||
LocalDateTime.of(2026, 5, 29, 15, 0, 0),
|
windowEndExclusive,
|
||||||
16
|
16
|
||||||
)
|
)
|
||||||
).thenReturn(emptyList())
|
).thenReturn(emptyList())
|
||||||
@@ -179,8 +179,8 @@ class RecommendationSnapshotRefreshServiceTest {
|
|||||||
)
|
)
|
||||||
Mockito.`when`(
|
Mockito.`when`(
|
||||||
queryPort.findCheerCreatorSnapshots(
|
queryPort.findCheerCreatorSnapshots(
|
||||||
LocalDateTime.of(2026, 5, 28, 15, 0, 0),
|
windowStart,
|
||||||
aiWindowEndExclusive,
|
windowEndExclusive,
|
||||||
16
|
16
|
||||||
)
|
)
|
||||||
).thenReturn(
|
).thenReturn(
|
||||||
@@ -229,8 +229,8 @@ class RecommendationSnapshotRefreshServiceTest {
|
|||||||
).thenReturn(emptyList())
|
).thenReturn(emptyList())
|
||||||
Mockito.`when`(
|
Mockito.`when`(
|
||||||
queryPort.findCheerCreatorSnapshots(
|
queryPort.findCheerCreatorSnapshots(
|
||||||
LocalDateTime.of(2026, 5, 28, 15, 0, 0),
|
windowStart,
|
||||||
LocalDateTime.of(2026, 5, 29, 15, 0, 0),
|
windowEndExclusive,
|
||||||
16
|
16
|
||||||
)
|
)
|
||||||
).thenReturn(emptyList())
|
).thenReturn(emptyList())
|
||||||
@@ -264,8 +264,6 @@ class RecommendationSnapshotRefreshServiceTest {
|
|||||||
val windowStart = LocalDateTime.of(2026, 5, 22, 15, 0, 0)
|
val windowStart = LocalDateTime.of(2026, 5, 22, 15, 0, 0)
|
||||||
val windowEndExclusive = LocalDateTime.of(2026, 5, 29, 15, 0, 0)
|
val windowEndExclusive = LocalDateTime.of(2026, 5, 29, 15, 0, 0)
|
||||||
val snapshotAt = LocalDateTime.of(2026, 5, 29, 14, 59, 59)
|
val snapshotAt = LocalDateTime.of(2026, 5, 29, 14, 59, 59)
|
||||||
val cheerWindowStart = LocalDateTime.of(2026, 5, 28, 15, 0, 0)
|
|
||||||
val cheerWindowEndExclusive = LocalDateTime.of(2026, 5, 29, 15, 0, 0)
|
|
||||||
Mockito.`when`(redissonClient.getLock(RecommendationSnapshotFallbackService.AI_CHARACTER_LOCK_KEY)).thenReturn(aiLock)
|
Mockito.`when`(redissonClient.getLock(RecommendationSnapshotFallbackService.AI_CHARACTER_LOCK_KEY)).thenReturn(aiLock)
|
||||||
Mockito.`when`(redissonClient.getLock(RecommendationSnapshotFallbackService.CHEER_CREATOR_LOCK_KEY)).thenReturn(cheerLock)
|
Mockito.`when`(redissonClient.getLock(RecommendationSnapshotFallbackService.CHEER_CREATOR_LOCK_KEY)).thenReturn(cheerLock)
|
||||||
Mockito.`when`(redissonClient.getLock(RecommendationSnapshotFallbackService.POPULAR_COMMUNITY_LOCK_KEY))
|
Mockito.`when`(redissonClient.getLock(RecommendationSnapshotFallbackService.POPULAR_COMMUNITY_LOCK_KEY))
|
||||||
@@ -276,7 +274,7 @@ class RecommendationSnapshotRefreshServiceTest {
|
|||||||
Mockito.`when`(cheerLock.isHeldByCurrentThread).thenReturn(true)
|
Mockito.`when`(cheerLock.isHeldByCurrentThread).thenReturn(true)
|
||||||
Mockito.`when`(popularLock.tryLock(0, -1, TimeUnit.MILLISECONDS)).thenReturn(true)
|
Mockito.`when`(popularLock.tryLock(0, -1, TimeUnit.MILLISECONDS)).thenReturn(true)
|
||||||
Mockito.`when`(popularLock.isHeldByCurrentThread).thenReturn(true)
|
Mockito.`when`(popularLock.isHeldByCurrentThread).thenReturn(true)
|
||||||
Mockito.`when`(queryPort.findCheerCreatorSnapshots(cheerWindowStart, cheerWindowEndExclusive, 16)).thenReturn(
|
Mockito.`when`(queryPort.findCheerCreatorSnapshots(windowStart, windowEndExclusive, 16)).thenReturn(
|
||||||
listOf(snapshot(RecommendedSectionType.CHEER_CREATOR, targetId = 10L, score = 10.0, snapshotAt = snapshotAt))
|
listOf(snapshot(RecommendedSectionType.CHEER_CREATOR, targetId = 10L, score = 10.0, snapshotAt = snapshotAt))
|
||||||
)
|
)
|
||||||
Mockito.`when`(queryPort.findPopularCommunitySnapshots(windowStart, windowEndExclusive, 20)).thenReturn(emptyList())
|
Mockito.`when`(queryPort.findPopularCommunitySnapshots(windowStart, windowEndExclusive, 20)).thenReturn(emptyList())
|
||||||
@@ -285,7 +283,7 @@ class RecommendationSnapshotRefreshServiceTest {
|
|||||||
|
|
||||||
Mockito.verify(redissonClient).getLock(RecommendationSnapshotFallbackService.CHEER_CREATOR_LOCK_KEY)
|
Mockito.verify(redissonClient).getLock(RecommendationSnapshotFallbackService.CHEER_CREATOR_LOCK_KEY)
|
||||||
Mockito.verify(cheerLock).tryLock(0, -1, TimeUnit.MILLISECONDS)
|
Mockito.verify(cheerLock).tryLock(0, -1, TimeUnit.MILLISECONDS)
|
||||||
Mockito.verify(queryPort).findCheerCreatorSnapshots(cheerWindowStart, cheerWindowEndExclusive, 16)
|
Mockito.verify(queryPort).findCheerCreatorSnapshots(windowStart, windowEndExclusive, 16)
|
||||||
assertEquals(listOf(10L), snapshotPort.findLatestSnapshots(RecommendedSectionType.CHEER_CREATOR).map { it.targetId })
|
assertEquals(listOf(10L), snapshotPort.findLatestSnapshots(RecommendedSectionType.CHEER_CREATOR).map { it.targetId })
|
||||||
Mockito.verify(cheerLock).unlock()
|
Mockito.verify(cheerLock).unlock()
|
||||||
Mockito.verify(popularLock).unlock()
|
Mockito.verify(popularLock).unlock()
|
||||||
@@ -322,8 +320,8 @@ class RecommendationSnapshotRefreshServiceTest {
|
|||||||
service.refreshDailySnapshots(now)
|
service.refreshDailySnapshots(now)
|
||||||
|
|
||||||
Mockito.verify(queryPort, Mockito.never()).findCheerCreatorSnapshots(
|
Mockito.verify(queryPort, Mockito.never()).findCheerCreatorSnapshots(
|
||||||
LocalDateTime.of(2026, 5, 28, 15, 0, 0),
|
windowStart,
|
||||||
LocalDateTime.of(2026, 5, 29, 15, 0, 0),
|
windowEndExclusive,
|
||||||
16
|
16
|
||||||
)
|
)
|
||||||
assertEquals(
|
assertEquals(
|
||||||
@@ -361,8 +359,8 @@ class RecommendationSnapshotRefreshServiceTest {
|
|||||||
Mockito.`when`(popularLock.isHeldByCurrentThread).thenReturn(true)
|
Mockito.`when`(popularLock.isHeldByCurrentThread).thenReturn(true)
|
||||||
Mockito.`when`(
|
Mockito.`when`(
|
||||||
queryPort.findCheerCreatorSnapshots(
|
queryPort.findCheerCreatorSnapshots(
|
||||||
LocalDateTime.of(2026, 5, 28, 15, 0, 0),
|
windowStart,
|
||||||
LocalDateTime.of(2026, 5, 29, 15, 0, 0),
|
windowEndExclusive,
|
||||||
16
|
16
|
||||||
)
|
)
|
||||||
).thenReturn(
|
).thenReturn(
|
||||||
|
|||||||
Reference in New Issue
Block a user