feat(content-ranking): 랭킹 스냅샷 job 저장소를 추가한다
This commit is contained in:
+114
@@ -0,0 +1,114 @@
|
||||
package kr.co.vividnext.sodalive.v2.content.ranking.adapter.out.persistence
|
||||
|
||||
import kr.co.vividnext.sodalive.configs.QueryDslConfig
|
||||
import kr.co.vividnext.sodalive.v2.content.ranking.domain.AudioRankingType
|
||||
import kr.co.vividnext.sodalive.v2.content.ranking.port.out.AudioRankingSnapshotJobRecord
|
||||
import kr.co.vividnext.sodalive.v2.content.ranking.port.out.AudioRankingSnapshotJobStatus
|
||||
import kr.co.vividnext.sodalive.v2.content.ranking.port.out.AudioRankingSnapshotJobTrigger
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
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
|
||||
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 DefaultAudioRankingSnapshotJobRepositoryTest @Autowired constructor(
|
||||
private val repository: AudioRankingSnapshotJobRepository
|
||||
) {
|
||||
private val adapter = DefaultAudioRankingSnapshotJobRepository(repository)
|
||||
|
||||
@Test
|
||||
@DisplayName("스냅샷 job은 랭킹 타입, 기간, 트리거, 상태와 처리 정보를 저장하고 변경한다")
|
||||
fun shouldSaveAndUpdateSnapshotJobHistory() {
|
||||
val startAt = LocalDateTime.of(2026, 5, 31, 15, 0)
|
||||
val endAt = LocalDateTime.of(2026, 6, 7, 15, 0)
|
||||
val visibleAt = LocalDateTime.of(2026, 6, 8, 0, 0)
|
||||
val saved = adapter.save(jobRecord(startAt = startAt, endAt = endAt, visibleAt = visibleAt))
|
||||
val jobId = saved.id!!
|
||||
|
||||
adapter.markProcessing(jobId, LocalDateTime.of(2026, 6, 8, 1, 0))
|
||||
adapter.markFailed(jobId, LocalDateTime.of(2026, 6, 8, 1, 1), "aggregate failed")
|
||||
|
||||
val failed = adapter.findById(jobId)
|
||||
assertEquals(AudioRankingType.WEEKLY_POPULAR, failed?.rankingType)
|
||||
assertEquals(AudioRankingSnapshotJobTrigger.SCHEDULED, failed?.trigger)
|
||||
assertEquals(AudioRankingSnapshotJobStatus.FAILED, failed?.status)
|
||||
assertEquals("aggregate failed", failed?.lastError)
|
||||
|
||||
adapter.markProcessing(jobId, LocalDateTime.of(2026, 6, 8, 1, 2))
|
||||
adapter.markDone(jobId, LocalDateTime.of(2026, 6, 8, 1, 3))
|
||||
|
||||
val doneJobs = adapter.findByRankingTypeAndPeriodAndStatuses(
|
||||
rankingType = AudioRankingType.WEEKLY_POPULAR,
|
||||
aggregationStartAtUtc = startAt,
|
||||
aggregationEndAtUtc = endAt,
|
||||
statuses = listOf(AudioRankingSnapshotJobStatus.DONE)
|
||||
)
|
||||
assertEquals(1, doneJobs.size)
|
||||
assertEquals(AudioRankingSnapshotJobStatus.DONE, doneJobs.single().status)
|
||||
assertEquals(null, doneJobs.single().lastError)
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("fallback job 수는 랭킹 타입과 집계 기간과 FALLBACK 트리거 기준으로만 계산한다")
|
||||
fun shouldCountFallbackJobsByRankingTypeAndPeriodAndTrigger() {
|
||||
val startAt = LocalDateTime.of(2026, 5, 31, 15, 0)
|
||||
val endAt = LocalDateTime.of(2026, 6, 7, 15, 0)
|
||||
adapter.save(jobRecord(startAt = startAt, endAt = endAt, trigger = AudioRankingSnapshotJobTrigger.FALLBACK))
|
||||
adapter.save(jobRecord(startAt = startAt, endAt = endAt, trigger = AudioRankingSnapshotJobTrigger.FALLBACK))
|
||||
adapter.save(jobRecord(startAt = startAt, endAt = endAt, trigger = AudioRankingSnapshotJobTrigger.SCHEDULED))
|
||||
adapter.save(
|
||||
jobRecord(
|
||||
startAt = startAt,
|
||||
endAt = endAt,
|
||||
rankingType = AudioRankingType.RISING,
|
||||
trigger = AudioRankingSnapshotJobTrigger.FALLBACK
|
||||
)
|
||||
)
|
||||
adapter.save(
|
||||
jobRecord(
|
||||
startAt = startAt.minusWeeks(1),
|
||||
endAt = endAt.minusWeeks(1),
|
||||
trigger = AudioRankingSnapshotJobTrigger.FALLBACK
|
||||
)
|
||||
)
|
||||
|
||||
val count = adapter.countByRankingTypeAndPeriodAndTrigger(
|
||||
rankingType = AudioRankingType.WEEKLY_POPULAR,
|
||||
aggregationStartAtUtc = startAt,
|
||||
aggregationEndAtUtc = endAt,
|
||||
trigger = AudioRankingSnapshotJobTrigger.FALLBACK
|
||||
)
|
||||
|
||||
assertEquals(2L, count)
|
||||
}
|
||||
|
||||
private fun jobRecord(
|
||||
rankingType: AudioRankingType = AudioRankingType.WEEKLY_POPULAR,
|
||||
startAt: LocalDateTime,
|
||||
endAt: LocalDateTime,
|
||||
visibleAt: LocalDateTime = LocalDateTime.of(2026, 6, 8, 0, 0),
|
||||
trigger: AudioRankingSnapshotJobTrigger = AudioRankingSnapshotJobTrigger.SCHEDULED,
|
||||
status: AudioRankingSnapshotJobStatus = AudioRankingSnapshotJobStatus.PENDING
|
||||
): AudioRankingSnapshotJobRecord {
|
||||
return AudioRankingSnapshotJobRecord(
|
||||
rankingType = rankingType,
|
||||
aggregationStartAtUtc = startAt,
|
||||
aggregationEndAtUtc = endAt,
|
||||
visibleFromAtUtc = visibleAt,
|
||||
trigger = trigger,
|
||||
status = status,
|
||||
lastError = null,
|
||||
processingStartedAt = null,
|
||||
processedAt = null
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user