feat(recommend): 추천 스냅샷 저장소를 추가한다

This commit is contained in:
2026-05-31 00:57:15 +09:00
parent a7e17fede2
commit 2edd486524
5 changed files with 224 additions and 0 deletions

View File

@@ -0,0 +1,109 @@
package kr.co.vividnext.sodalive.v2.recommend.adapter.out.persistence
import kr.co.vividnext.sodalive.configs.QueryDslConfig
import kr.co.vividnext.sodalive.v2.recommend.domain.RecommendedSectionType
import kr.co.vividnext.sodalive.v2.recommend.port.out.RecommendationSnapshotRecord
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest
import org.springframework.context.annotation.Import
import java.time.LocalDateTime
@DataJpaTest(
properties = [
"spring.cache.type=none",
"spring.datasource.url=jdbc:h2:mem:testdb;MODE=MySQL;NON_KEYWORDS=VALUE"
]
)
@Import(QueryDslConfig::class)
class RecommendationSnapshotPersistenceAdapterTest @Autowired constructor(
private val repository: RecommendationSnapshotRepository
) {
private val adapter = RecommendationSnapshotPersistenceAdapter(repository)
@Test
fun shouldFindLatestSnapshotsByLatestSnapshotAtAndScoreDescendingTieBreakerAscending() {
val oldSnapshotAt = LocalDateTime.of(2026, 5, 28, 23, 59, 59)
val latestSnapshotAt = LocalDateTime.of(2026, 5, 29, 23, 59, 59)
repository.saveAll(
listOf(
snapshot(RecommendedSectionType.AI_CHARACTER, targetId = 1L, score = 999.0, snapshotAt = oldSnapshotAt),
snapshot(
RecommendedSectionType.AI_CHARACTER,
targetId = 2L,
score = 100.0,
snapshotAt = latestSnapshotAt,
randomTieBreaker = 0.9
),
snapshot(
RecommendedSectionType.AI_CHARACTER,
targetId = 3L,
score = 200.0,
snapshotAt = latestSnapshotAt,
randomTieBreaker = 0.8
),
snapshot(
RecommendedSectionType.AI_CHARACTER,
targetId = 4L,
score = 100.0,
snapshotAt = latestSnapshotAt,
randomTieBreaker = 0.1
),
snapshot(RecommendedSectionType.CHEER_CREATOR, targetId = 5L, score = 300.0, snapshotAt = latestSnapshotAt)
)
)
val latestSnapshots = adapter.findLatestSnapshots(RecommendedSectionType.AI_CHARACTER)
assertEquals(listOf(3L, 4L, 2L), latestSnapshots.map { it.targetId })
assertEquals(listOf(latestSnapshotAt, latestSnapshotAt, latestSnapshotAt), latestSnapshots.map { it.snapshotAt })
}
@Test
fun shouldReplaceSnapshotsByDeletingSameSectionSnapshotAtOnly() {
val oldSnapshotAt = LocalDateTime.of(2026, 5, 28, 23, 59, 59)
val snapshotAt = LocalDateTime.of(2026, 5, 29, 23, 59, 59)
repository.saveAll(
listOf(
snapshot(RecommendedSectionType.AI_CHARACTER, targetId = 1L, score = 100.0, snapshotAt = oldSnapshotAt),
snapshot(RecommendedSectionType.AI_CHARACTER, targetId = 2L, score = 200.0, snapshotAt = snapshotAt),
snapshot(RecommendedSectionType.CHEER_CREATOR, targetId = 3L, score = 300.0, snapshotAt = snapshotAt)
)
)
adapter.replaceSnapshots(
RecommendedSectionType.AI_CHARACTER,
snapshotAt,
listOf(
RecommendationSnapshotRecord(
sectionType = RecommendedSectionType.AI_CHARACTER,
targetId = 4L,
score = 400.0,
snapshotAt = snapshotAt,
randomTieBreaker = 0.4
)
)
)
assertEquals(listOf(4L), adapter.findLatestSnapshots(RecommendedSectionType.AI_CHARACTER).map { it.targetId })
assertEquals(listOf(3L), adapter.findLatestSnapshots(RecommendedSectionType.CHEER_CREATOR).map { it.targetId })
assertEquals(3, repository.findAll().size)
}
private fun snapshot(
sectionType: RecommendedSectionType,
targetId: Long,
score: Double,
snapshotAt: LocalDateTime,
randomTieBreaker: Double = 0.1
): RecommendationSnapshot {
return RecommendationSnapshot(
sectionType = sectionType,
targetId = targetId,
score = score,
snapshotAt = snapshotAt,
randomTieBreaker = randomTieBreaker
)
}
}