test #426
@@ -0,0 +1,34 @@
|
|||||||
|
package kr.co.vividnext.sodalive.v2.api.creator.channel.series.application
|
||||||
|
|
||||||
|
import kr.co.vividnext.sodalive.member.Member
|
||||||
|
import kr.co.vividnext.sodalive.v2.api.creator.channel.series.dto.CreatorChannelSeriesTabResponse
|
||||||
|
import kr.co.vividnext.sodalive.v2.creator.channel.series.application.CreatorChannelSeriesQueryService
|
||||||
|
import org.springframework.stereotype.Service
|
||||||
|
import org.springframework.transaction.annotation.Transactional
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
class CreatorChannelSeriesFacade(
|
||||||
|
private val creatorChannelSeriesQueryService: CreatorChannelSeriesQueryService
|
||||||
|
) {
|
||||||
|
fun getSeriesTab(
|
||||||
|
creatorId: Long,
|
||||||
|
viewer: Member,
|
||||||
|
sort: String?,
|
||||||
|
page: Int?,
|
||||||
|
size: Int?,
|
||||||
|
now: LocalDateTime = LocalDateTime.now()
|
||||||
|
): CreatorChannelSeriesTabResponse {
|
||||||
|
return CreatorChannelSeriesTabResponse.from(
|
||||||
|
creatorChannelSeriesQueryService.getSeriesTab(
|
||||||
|
creatorId = creatorId,
|
||||||
|
viewer = viewer,
|
||||||
|
sort = sort,
|
||||||
|
page = page,
|
||||||
|
size = size,
|
||||||
|
now = now
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
package kr.co.vividnext.sodalive.v2.api.creator.channel.series.dto
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty
|
||||||
|
import kr.co.vividnext.sodalive.v2.common.domain.ContentSort
|
||||||
|
import kr.co.vividnext.sodalive.v2.creator.channel.series.domain.CreatorChannelSeries
|
||||||
|
import kr.co.vividnext.sodalive.v2.creator.channel.series.domain.CreatorChannelSeriesTab
|
||||||
|
|
||||||
|
data class CreatorChannelSeriesTabResponse(
|
||||||
|
val seriesCount: Int,
|
||||||
|
val series: List<CreatorChannelSeriesResponse>,
|
||||||
|
val sort: ContentSort,
|
||||||
|
val page: Int,
|
||||||
|
val size: Int,
|
||||||
|
@JsonProperty("hasNext")
|
||||||
|
val hasNext: Boolean
|
||||||
|
) {
|
||||||
|
companion object {
|
||||||
|
fun from(tab: CreatorChannelSeriesTab): CreatorChannelSeriesTabResponse {
|
||||||
|
return CreatorChannelSeriesTabResponse(
|
||||||
|
seriesCount = tab.seriesCount,
|
||||||
|
series = tab.series.map(CreatorChannelSeriesResponse::from),
|
||||||
|
sort = tab.sort,
|
||||||
|
page = tab.page.page,
|
||||||
|
size = tab.page.size,
|
||||||
|
hasNext = tab.hasNext
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class CreatorChannelSeriesResponse(
|
||||||
|
val seriesId: Long,
|
||||||
|
val title: String,
|
||||||
|
val coverImageUrl: String?,
|
||||||
|
val publishedDaysOfWeek: String,
|
||||||
|
@JsonProperty("isOriginal")
|
||||||
|
val isOriginal: Boolean,
|
||||||
|
@JsonProperty("isAdult")
|
||||||
|
val isAdult: Boolean,
|
||||||
|
@JsonProperty("isProceeding")
|
||||||
|
val isProceeding: Boolean,
|
||||||
|
val contentCount: Int,
|
||||||
|
val purchasedContentCount: Int?,
|
||||||
|
val paidContentCount: Int?,
|
||||||
|
val purchasedPaidContentRate: Int?
|
||||||
|
) {
|
||||||
|
companion object {
|
||||||
|
fun from(series: CreatorChannelSeries): CreatorChannelSeriesResponse {
|
||||||
|
return CreatorChannelSeriesResponse(
|
||||||
|
seriesId = series.seriesId,
|
||||||
|
title = series.title,
|
||||||
|
coverImageUrl = series.coverImageUrl,
|
||||||
|
publishedDaysOfWeek = series.publishedDaysOfWeek,
|
||||||
|
isOriginal = series.isOriginal,
|
||||||
|
isAdult = series.isAdult,
|
||||||
|
isProceeding = series.isProceeding,
|
||||||
|
contentCount = series.contentCount,
|
||||||
|
purchasedContentCount = series.purchasedContentCount,
|
||||||
|
paidContentCount = series.paidContentCount,
|
||||||
|
purchasedPaidContentRate = series.purchasedPaidContentRate
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,133 @@
|
|||||||
|
package kr.co.vividnext.sodalive.v2.api.creator.channel.series.application
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper
|
||||||
|
import com.fasterxml.jackson.module.kotlin.KotlinModule
|
||||||
|
import kr.co.vividnext.sodalive.member.Member
|
||||||
|
import kr.co.vividnext.sodalive.member.MemberRole
|
||||||
|
import kr.co.vividnext.sodalive.v2.api.creator.channel.series.dto.CreatorChannelSeriesTabResponse
|
||||||
|
import kr.co.vividnext.sodalive.v2.common.domain.ContentSort
|
||||||
|
import kr.co.vividnext.sodalive.v2.creator.channel.live.domain.CreatorChannelPage
|
||||||
|
import kr.co.vividnext.sodalive.v2.creator.channel.series.application.CreatorChannelSeriesQueryService
|
||||||
|
import kr.co.vividnext.sodalive.v2.creator.channel.series.domain.CreatorChannelSeries
|
||||||
|
import kr.co.vividnext.sodalive.v2.creator.channel.series.domain.CreatorChannelSeriesTab
|
||||||
|
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
|
||||||
|
import org.mockito.Mockito
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
|
class CreatorChannelSeriesFacadeTest {
|
||||||
|
@Test
|
||||||
|
@DisplayName("시리즈 탭 응답 DTO는 domain tab 값을 공개 응답 필드로 그대로 매핑한다")
|
||||||
|
fun shouldMapSeriesTabDomainToPublicResponse() {
|
||||||
|
val response = CreatorChannelSeriesTabResponse.from(createTab())
|
||||||
|
|
||||||
|
assertEquals(2, response.seriesCount)
|
||||||
|
assertEquals(101L, response.series.first().seriesId)
|
||||||
|
assertEquals("series", response.series.first().title)
|
||||||
|
assertEquals("https://cdn.test/cover.png", response.series.first().coverImageUrl)
|
||||||
|
assertEquals("Every Mon, Thu", response.series.first().publishedDaysOfWeek)
|
||||||
|
assertTrue(response.series.first().isOriginal)
|
||||||
|
assertFalse(response.series.first().isAdult)
|
||||||
|
assertTrue(response.series.first().isProceeding)
|
||||||
|
assertEquals(5, response.series.first().contentCount)
|
||||||
|
assertEquals(3, response.series.first().purchasedContentCount)
|
||||||
|
assertEquals(4, response.series.first().paidContentCount)
|
||||||
|
assertEquals(75, response.series.first().purchasedPaidContentRate)
|
||||||
|
assertEquals(ContentSort.OWNED, response.sort)
|
||||||
|
assertEquals(1, response.page)
|
||||||
|
assertEquals(20, response.size)
|
||||||
|
assertTrue(response.hasNext)
|
||||||
|
|
||||||
|
val mapper = ObjectMapper().registerModule(KotlinModule.Builder().build())
|
||||||
|
val json = mapper.readTree(mapper.writeValueAsString(response))
|
||||||
|
assertTrue(json["hasNext"].asBoolean())
|
||||||
|
assertTrue(json["series"][0]["isOriginal"].asBoolean())
|
||||||
|
assertFalse(json["series"][0]["isAdult"].asBoolean())
|
||||||
|
assertTrue(json["series"][0]["isProceeding"].asBoolean())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("시리즈 탭 facade는 query service 결과를 공개 응답 DTO로 변환한다")
|
||||||
|
fun shouldMapSeriesTabQueryResultToPublicResponse() {
|
||||||
|
val service = Mockito.mock(CreatorChannelSeriesQueryService::class.java)
|
||||||
|
val facade = CreatorChannelSeriesFacade(service)
|
||||||
|
val viewer = createMember(id = 10L)
|
||||||
|
val now = LocalDateTime.of(2026, 6, 20, 10, 0)
|
||||||
|
Mockito.doReturn(createTab()).`when`(service).getSeriesTab(
|
||||||
|
creatorId = 1L,
|
||||||
|
viewer = viewer,
|
||||||
|
sort = "OWNED",
|
||||||
|
page = 1,
|
||||||
|
size = 20,
|
||||||
|
now = now
|
||||||
|
)
|
||||||
|
|
||||||
|
val response = facade.getSeriesTab(
|
||||||
|
creatorId = 1L,
|
||||||
|
viewer = viewer,
|
||||||
|
sort = "OWNED",
|
||||||
|
page = 1,
|
||||||
|
size = 20,
|
||||||
|
now = now
|
||||||
|
)
|
||||||
|
|
||||||
|
assertEquals(2, response.seriesCount)
|
||||||
|
assertEquals(101L, response.series.first().seriesId)
|
||||||
|
assertEquals(75, response.series.first().purchasedPaidContentRate)
|
||||||
|
assertEquals(ContentSort.OWNED, response.sort)
|
||||||
|
assertEquals(1, response.page)
|
||||||
|
assertEquals(20, response.size)
|
||||||
|
assertTrue(response.hasNext)
|
||||||
|
assertNull(response.series.last().purchasedPaidContentRate)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createMember(id: Long): Member {
|
||||||
|
return Member(
|
||||||
|
email = "viewer$id@test.com",
|
||||||
|
password = "password",
|
||||||
|
nickname = "viewer$id",
|
||||||
|
role = MemberRole.USER
|
||||||
|
).apply { this.id = id }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createTab(): CreatorChannelSeriesTab {
|
||||||
|
return CreatorChannelSeriesTab(
|
||||||
|
seriesCount = 2,
|
||||||
|
series = listOf(
|
||||||
|
CreatorChannelSeries(
|
||||||
|
seriesId = 101L,
|
||||||
|
title = "series",
|
||||||
|
coverImageUrl = "https://cdn.test/cover.png",
|
||||||
|
publishedDaysOfWeek = "Every Mon, Thu",
|
||||||
|
isOriginal = true,
|
||||||
|
isAdult = false,
|
||||||
|
isProceeding = true,
|
||||||
|
contentCount = 5,
|
||||||
|
purchasedContentCount = 3,
|
||||||
|
paidContentCount = 4,
|
||||||
|
purchasedPaidContentRate = 75
|
||||||
|
),
|
||||||
|
CreatorChannelSeries(
|
||||||
|
seriesId = 102L,
|
||||||
|
title = "creator series",
|
||||||
|
coverImageUrl = null,
|
||||||
|
publishedDaysOfWeek = "Random",
|
||||||
|
isOriginal = false,
|
||||||
|
isAdult = false,
|
||||||
|
isProceeding = false,
|
||||||
|
contentCount = 1,
|
||||||
|
purchasedContentCount = null,
|
||||||
|
paidContentCount = null,
|
||||||
|
purchasedPaidContentRate = null
|
||||||
|
)
|
||||||
|
),
|
||||||
|
sort = ContentSort.OWNED,
|
||||||
|
page = CreatorChannelPage(page = 1, size = 20),
|
||||||
|
hasNext = true
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user