feat(content): 랭킹 응답 매핑을 추가한다

This commit is contained in:
2026-06-24 13:35:28 +09:00
parent 0d0bec1904
commit f2996f599a
2 changed files with 168 additions and 0 deletions

View File

@@ -0,0 +1,40 @@
package kr.co.vividnext.sodalive.v2.main.content.model
import kr.co.vividnext.sodalive.v2.main.content.data.AudioRankingItemResponse
import kr.co.vividnext.sodalive.v2.main.content.data.AudioRankingResponse
import kr.co.vividnext.sodalive.v2.widget.contentranking.ContentRankingItem
import kr.co.vividnext.sodalive.v2.widget.ranking.RankingChangeType
import kr.co.vividnext.sodalive.v2.widget.ranking.RankingChangeType.Decrease
import kr.co.vividnext.sodalive.v2.widget.ranking.RankingChangeType.Increase
import kr.co.vividnext.sodalive.v2.widget.ranking.RankingChangeType.New
import kr.co.vividnext.sodalive.v2.widget.ranking.RankingChangeType.Stay
import kotlin.math.abs
fun AudioRankingResponse.toContentRankingItems(): List<ContentRankingItem> = items
.filter { it.rank >= 1 }
.sortedBy { it.rank }
.map { it.toContentRankingItem(showRankChange) }
private fun AudioRankingItemResponse.toContentRankingItem(showRankChange: Boolean): ContentRankingItem {
val changeType = toRankingChangeType()
return ContentRankingItem(
contentId = contentId.toString(),
creatorId = "",
rank = rank,
previousRank = null,
rankChangeType = changeType,
rankChangeAmount = if (changeType == New) 0 else abs(rankChange ?: 0),
contentName = title,
creatorName = creatorNickname,
imageUrl = coverImageUrl.orEmpty(),
isBlocked = false,
showRankChange = showRankChange
)
}
private fun AudioRankingItemResponse.toRankingChangeType(): RankingChangeType = when {
isNew -> New
rankChange == null || rankChange == 0 -> Stay
rankChange > 0 -> Increase
else -> Decrease
}

View File

@@ -0,0 +1,128 @@
package kr.co.vividnext.sodalive.v2.main.content
import kr.co.vividnext.sodalive.v2.main.content.data.AudioRankingItemResponse
import kr.co.vividnext.sodalive.v2.main.content.data.AudioRankingResponse
import kr.co.vividnext.sodalive.v2.main.content.data.AudioRankingType
import kr.co.vividnext.sodalive.v2.main.content.model.toContentRankingItems
import kr.co.vividnext.sodalive.v2.widget.ranking.RankingChangeType.Decrease
import kr.co.vividnext.sodalive.v2.widget.ranking.RankingChangeType.Increase
import kr.co.vividnext.sodalive.v2.widget.ranking.RankingChangeType.New
import kr.co.vividnext.sodalive.v2.widget.ranking.RankingChangeType.Stay
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Test
class AudioRankingsMapperTest {
@Test
fun `응답 아이템은 rank 오름차순으로 정렬하고 유효하지 않은 rank는 제외한다`() {
val items = response(
items = listOf(
rankingItem(contentId = 3L, rank = 3),
rankingItem(contentId = 0L, rank = 0),
rankingItem(contentId = 1L, rank = 1),
rankingItem(contentId = 2L, rank = 2)
)
).toContentRankingItems()
assertEquals(listOf(1, 2, 3), items.map { it.rank })
assertEquals(listOf("1", "2", "3"), items.map { it.contentId })
}
@Test
fun `isNew는 rankChange보다 우선한다`() {
val item = response(
items = listOf(rankingItem(isNew = true, rankChange = -3))
).toContentRankingItems().single()
assertEquals(New, item.rankChangeType)
assertEquals(0, item.rankChangeAmount)
}
@Test
fun `rankChange가 null이거나 0이면 유지로 매핑한다`() {
val items = response(
items = listOf(
rankingItem(rank = 1, rankChange = null),
rankingItem(rank = 2, rankChange = 0)
)
).toContentRankingItems()
assertEquals(listOf(Stay, Stay), items.map { it.rankChangeType })
assertEquals(listOf(0, 0), items.map { it.rankChangeAmount })
}
@Test
fun `양수와 음수 rankChange는 절대값과 함께 상승과 하락으로 매핑한다`() {
val items = response(
items = listOf(
rankingItem(rank = 1, rankChange = 5),
rankingItem(rank = 2, rankChange = -3)
)
).toContentRankingItems()
assertEquals(listOf(Increase, Decrease), items.map { it.rankChangeType })
assertEquals(listOf(5, 3), items.map { it.rankChangeAmount })
}
@Test
fun `showRankChange false는 모든 콘텐츠 랭킹 아이템에 전달된다`() {
val items = response(
showRankChange = false,
items = listOf(rankingItem(rank = 1), rankingItem(rank = 2))
).toContentRankingItems()
assertTrue(items.isNotEmpty())
assertTrue(items.all { !it.showRankChange })
}
@Test
fun `nullable 필드와 누락된 UI 필드는 콘텐츠 랭킹 기본값으로 매핑한다`() {
val item = response(
items = listOf(
rankingItem(
contentId = 10L,
title = "오디오 콘텐츠",
creatorNickname = "크리에이터",
coverImageUrl = null
)
)
).toContentRankingItems().single()
assertEquals("10", item.contentId)
assertEquals("", item.creatorId)
assertEquals("오디오 콘텐츠", item.contentName)
assertEquals("크리에이터", item.creatorName)
assertEquals("", item.imageUrl)
assertFalse(item.isBlocked)
}
private fun response(
showRankChange: Boolean = true,
type: AudioRankingType = AudioRankingType.WEEKLY_POPULAR,
items: List<AudioRankingItemResponse> = listOf(rankingItem())
) = AudioRankingResponse(
showRankChange = showRankChange,
type = type,
items = items
)
private fun rankingItem(
contentId: Long = 1L,
title: String = "콘텐츠 이름",
creatorNickname: String = "크리에이터 이름",
rank: Int = 1,
rankChange: Int? = 1,
isNew: Boolean = false,
coverImageUrl: String? = "https://example.com/image.png"
) = AudioRankingItemResponse(
contentId = contentId,
title = title,
creatorNickname = creatorNickname,
rank = rank,
rankChange = rankChange,
isNew = isNew,
coverImageUrl = coverImageUrl
)
}