feat(creator-channel): 시리즈 탭 조회 정책을 추가한다

This commit is contained in:
2026-06-20 03:19:41 +09:00
parent 3d88dc7b8a
commit 2ebc728656
2 changed files with 278 additions and 0 deletions

View File

@@ -0,0 +1,150 @@
package kr.co.vividnext.sodalive.v2.creator.channel.series.domain
import kr.co.vividnext.sodalive.creator.admin.content.series.SeriesPublishedDaysOfWeek
import kr.co.vividnext.sodalive.creator.admin.content.series.SeriesState
import kr.co.vividnext.sodalive.member.MemberRole
import kr.co.vividnext.sodalive.v2.common.domain.ContentSort
import kr.co.vividnext.sodalive.v2.creator.channel.series.port.out.CreatorChannelSeriesCreatorRecord
import kr.co.vividnext.sodalive.v2.creator.channel.series.port.out.CreatorChannelSeriesRecord
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertFalse
import org.junit.jupiter.api.Assertions.assertNull
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
class CreatorChannelSeriesQueryPolicyTest {
private val policy = CreatorChannelSeriesQueryPolicy()
@Test
@DisplayName("시리즈 탭 sort 정책은 null과 알 수 없는 값을 LATEST로 fallback한다")
fun shouldFallbackInvalidSortToLatest() {
assertEquals(ContentSort.LATEST, policy.resolveSort(null))
assertEquals(ContentSort.LATEST, policy.resolveSort("UNKNOWN"))
assertEquals(ContentSort.POPULAR, policy.resolveSort("POPULAR"))
}
@Test
@DisplayName("시리즈 탭 page 정책은 page와 size를 fallback하고 fetch limit을 계산한다")
fun shouldFallbackPageAndSizeForSeriesTab() {
val minimumPage = policy.createPage(page = -1, size = 10)
val maximumPage = policy.createPage(page = 2, size = 100)
assertEquals(0, minimumPage.page)
assertEquals(20, minimumPage.size)
assertEquals(0L, minimumPage.offset)
assertEquals(21, minimumPage.fetchLimit)
assertEquals(2, maximumPage.page)
assertEquals(50, maximumPage.size)
assertEquals(100L, maximumPage.offset)
assertEquals(51, maximumPage.fetchLimit)
}
@Test
@DisplayName("시리즈 탭 목록 정책은 요청 size만 남기고 다음 페이지 여부를 계산한다")
fun shouldLimitItemsAndCalculateHasNext() {
val page = policy.createPage(page = 0, size = 20)
val fetched = (1..21).toList()
val items = policy.limitItems(fetched, page)
assertEquals((1..20).toList(), items)
assertTrue(policy.hasNext(fetched, page))
assertFalse(policy.hasNext((1..20).toList(), page))
}
@Test
@DisplayName("시리즈 탭 구매율은 유료 콘텐츠가 없으면 0이고 있으면 정수 백분율로 계산한다")
fun shouldCalculatePurchaseRateAsInteger() {
assertEquals(0, policy.purchaseRate(paidContentCount = 0, purchasedContentCount = 3))
assertEquals(75, policy.purchaseRate(paidContentCount = 4, purchasedContentCount = 3))
assertEquals(66, policy.purchaseRate(paidContentCount = 3, purchasedContentCount = 2))
}
@Test
@DisplayName("시리즈 탭 연재 요일은 RANDOM 포함 시 다른 요일을 무시하고 locale별 랜덤 문구를 반환한다")
fun shouldReturnRandomTextWhenDaysContainRandom() {
val days = setOf(SeriesPublishedDaysOfWeek.RANDOM, SeriesPublishedDaysOfWeek.MON)
assertEquals("랜덤", policy.publishedDaysOfWeekText(days, "ko"))
assertEquals("Random", policy.publishedDaysOfWeekText(days, "en"))
assertEquals("ランダム", policy.publishedDaysOfWeekText(days, "ja"))
}
@Test
@DisplayName("시리즈 탭 연재 요일은 7개 요일이면 locale별 매일 문구를 반환한다")
fun shouldReturnEveryDayTextWhenDaysContainAllWeekdays() {
val days = setOf(
SeriesPublishedDaysOfWeek.SUN,
SeriesPublishedDaysOfWeek.MON,
SeriesPublishedDaysOfWeek.TUE,
SeriesPublishedDaysOfWeek.WED,
SeriesPublishedDaysOfWeek.THU,
SeriesPublishedDaysOfWeek.FRI,
SeriesPublishedDaysOfWeek.SAT
)
assertEquals("매일", policy.publishedDaysOfWeekText(days, "ko"))
assertEquals("Every day", policy.publishedDaysOfWeekText(days, "en"))
assertEquals("毎日", policy.publishedDaysOfWeekText(days, "ja"))
}
@Test
@DisplayName("시리즈 탭 연재 요일은 SUN부터 SAT 순서로 locale별 매주 문구를 반환한다")
fun shouldReturnWeeklyTextOrderedFromSundayToSaturday() {
val days = setOf(SeriesPublishedDaysOfWeek.SAT, SeriesPublishedDaysOfWeek.MON, SeriesPublishedDaysOfWeek.THU)
assertEquals("매주 월, 목, 토", policy.publishedDaysOfWeekText(days, "ko"))
assertEquals("Every Mon, Thu, Sat", policy.publishedDaysOfWeekText(days, "en"))
assertEquals("毎週 月, 木, 土", policy.publishedDaysOfWeekText(days, "ja"))
}
@Test
@DisplayName("시리즈 탭 domain model과 port record는 Phase 1 계약 필드를 유지한다")
fun shouldKeepDomainAndPortContract() {
val tab = CreatorChannelSeriesTab(
seriesCount = 1,
series = listOf(
CreatorChannelSeries(
seriesId = 10L,
title = "title",
coverImageUrl = null,
publishedDaysOfWeek = "매일",
isOriginal = true,
isAdult = false,
isProceeding = true,
contentCount = 3,
purchasedContentCount = null,
paidContentCount = null,
purchasedPaidContentRate = null
)
),
sort = ContentSort.LATEST,
page = policy.createPage(page = 0, size = 20),
hasNext = false
)
val creatorRecord = CreatorChannelSeriesCreatorRecord(
creatorId = 1L,
role = MemberRole.CREATOR,
nickname = "creator"
)
val seriesRecord = CreatorChannelSeriesRecord(
seriesId = 10L,
title = "title",
coverImagePath = null,
publishedDaysOfWeek = setOf(SeriesPublishedDaysOfWeek.MON),
isOriginal = true,
isAdult = false,
state = SeriesState.PROCEEDING,
contentCount = 3,
purchasedContentCount = null,
paidContentCount = null
)
assertEquals(1, tab.seriesCount)
assertTrue(tab.series.first().isProceeding)
assertNull(tab.series.first().purchasedPaidContentRate)
assertEquals(MemberRole.CREATOR, creatorRecord.role)
assertEquals(setOf(SeriesPublishedDaysOfWeek.MON), seriesRecord.publishedDaysOfWeek)
}
}