test #426
@@ -116,6 +116,41 @@ class CreatorChannelLiveControllerTest @Autowired constructor(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("크리에이터 채널 라이브 탭 조회는 다음 페이지가 있는 대표 응답 표면을 반환한다")
|
||||||
|
fun shouldReturnLiveTabSurfaceWhenNextPageExists() {
|
||||||
|
val viewer = createMember(id = 10L)
|
||||||
|
Mockito.doReturn(createResponse(liveReplayContentCount = 21, contentCount = 20, hasNext = true))
|
||||||
|
.`when`(facade).getLiveTab(
|
||||||
|
eqValue(1L),
|
||||||
|
eqValue(viewer),
|
||||||
|
eqValue(ContentSort.LATEST),
|
||||||
|
eqValue(0),
|
||||||
|
eqValue(20),
|
||||||
|
anyValue(LocalDateTime.now())
|
||||||
|
)
|
||||||
|
|
||||||
|
mockMvc.perform(
|
||||||
|
get("/api/v2/creator-channels/1/live")
|
||||||
|
.with(user(MemberAdapter(viewer)))
|
||||||
|
)
|
||||||
|
.andExpect(status().isOk)
|
||||||
|
.andExpect(jsonPath("$.success").value(true))
|
||||||
|
.andExpect(jsonPath("$.data.liveReplayContentCount").value(21))
|
||||||
|
.andExpect(jsonPath("$.data.currentLive.liveId").value(101L))
|
||||||
|
.andExpect(jsonPath("$.data.liveReplayContents.length()").value(20))
|
||||||
|
.andExpect(jsonPath("$.data.sort").value("LATEST"))
|
||||||
|
.andExpect(jsonPath("$.data.page").value(0))
|
||||||
|
.andExpect(jsonPath("$.data.size").value(20))
|
||||||
|
.andExpect(jsonPath("$.data.hasNext").value(true))
|
||||||
|
.andExpect(jsonPath("$.data.liveReplayContents[0].isOwned").value(true))
|
||||||
|
.andExpect(jsonPath("$.data.liveReplayContents[0].isRented").value(false))
|
||||||
|
.andExpect(jsonPath("$.data.liveReplayContents[1].isOwned").value(false))
|
||||||
|
.andExpect(jsonPath("$.data.liveReplayContents[1].isRented").value(true))
|
||||||
|
.andExpect(jsonPath("$.data.liveReplayContents[2].isOwned").value(false))
|
||||||
|
.andExpect(jsonPath("$.data.liveReplayContents[2].isRented").value(false))
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("크리에이터 채널 라이브 탭 조회는 잘못된 page 요청을 기존 오류 응답으로 반환한다")
|
@DisplayName("크리에이터 채널 라이브 탭 조회는 잘못된 page 요청을 기존 오류 응답으로 반환한다")
|
||||||
fun shouldReturnErrorResponseWhenPageIsInvalid() {
|
fun shouldReturnErrorResponseWhenPageIsInvalid() {
|
||||||
@@ -181,9 +216,13 @@ class CreatorChannelLiveControllerTest @Autowired constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createResponse(): CreatorChannelLiveTabResponse {
|
private fun createResponse(
|
||||||
|
liveReplayContentCount: Int = 1,
|
||||||
|
contentCount: Int = 1,
|
||||||
|
hasNext: Boolean = false
|
||||||
|
): CreatorChannelLiveTabResponse {
|
||||||
return CreatorChannelLiveTabResponse(
|
return CreatorChannelLiveTabResponse(
|
||||||
liveReplayContentCount = 1,
|
liveReplayContentCount = liveReplayContentCount,
|
||||||
currentLive = CreatorChannelLiveResponse(
|
currentLive = CreatorChannelLiveResponse(
|
||||||
liveId = 101L,
|
liveId = 101L,
|
||||||
title = "live",
|
title = "live",
|
||||||
@@ -192,26 +231,30 @@ class CreatorChannelLiveControllerTest @Autowired constructor(
|
|||||||
price = 20,
|
price = 20,
|
||||||
isAdult = true
|
isAdult = true
|
||||||
),
|
),
|
||||||
liveReplayContents = listOf(
|
liveReplayContents = createAudioContents(contentCount),
|
||||||
CreatorChannelAudioContentResponse(
|
|
||||||
audioContentId = 201L,
|
|
||||||
title = "audio",
|
|
||||||
duration = "00:10:00",
|
|
||||||
imageUrl = "audio.png",
|
|
||||||
price = 30,
|
|
||||||
isAdult = false,
|
|
||||||
isPointAvailable = true,
|
|
||||||
isFirstContent = true,
|
|
||||||
seriesName = "series",
|
|
||||||
isOriginalSeries = true,
|
|
||||||
isOwned = true,
|
|
||||||
isRented = false
|
|
||||||
)
|
|
||||||
),
|
|
||||||
sort = ContentSort.LATEST,
|
sort = ContentSort.LATEST,
|
||||||
page = 0,
|
page = 0,
|
||||||
size = 20,
|
size = 20,
|
||||||
hasNext = false
|
hasNext = hasNext
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createAudioContents(count: Int): List<CreatorChannelAudioContentResponse> {
|
||||||
|
return (1..count).map { index ->
|
||||||
|
CreatorChannelAudioContentResponse(
|
||||||
|
audioContentId = 200L + index,
|
||||||
|
title = "audio-$index",
|
||||||
|
duration = "00:10:00",
|
||||||
|
imageUrl = "audio-$index.png",
|
||||||
|
price = 30,
|
||||||
|
isAdult = false,
|
||||||
|
isPointAvailable = true,
|
||||||
|
isFirstContent = index == 1,
|
||||||
|
seriesName = "series",
|
||||||
|
isOriginalSeries = true,
|
||||||
|
isOwned = index == 1,
|
||||||
|
isRented = index == 2
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,191 @@
|
|||||||
|
package kr.co.vividnext.sodalive.v2.api.creator.channel.live.adapter.`in`.web
|
||||||
|
|
||||||
|
import kr.co.vividnext.sodalive.content.AudioContent
|
||||||
|
import kr.co.vividnext.sodalive.content.order.Order
|
||||||
|
import kr.co.vividnext.sodalive.content.order.OrderType
|
||||||
|
import kr.co.vividnext.sodalive.content.theme.AudioContentTheme
|
||||||
|
import kr.co.vividnext.sodalive.live.room.GenderRestriction
|
||||||
|
import kr.co.vividnext.sodalive.live.room.LiveRoom
|
||||||
|
import kr.co.vividnext.sodalive.member.Member
|
||||||
|
import kr.co.vividnext.sodalive.member.MemberAdapter
|
||||||
|
import kr.co.vividnext.sodalive.member.MemberRole
|
||||||
|
import kr.co.vividnext.sodalive.support.EmbeddedRedisInitializer
|
||||||
|
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.web.servlet.AutoConfigureMockMvc
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest
|
||||||
|
import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user
|
||||||
|
import org.springframework.test.context.ContextConfiguration
|
||||||
|
import org.springframework.test.web.servlet.MockMvc
|
||||||
|
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get
|
||||||
|
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath
|
||||||
|
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status
|
||||||
|
import org.springframework.transaction.support.TransactionTemplate
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
import javax.persistence.EntityManager
|
||||||
|
|
||||||
|
@SpringBootTest(
|
||||||
|
properties = [
|
||||||
|
"cloud.aws.cloud-front.host=https://cdn.test",
|
||||||
|
"spring.cache.type=none",
|
||||||
|
"spring.datasource.url=jdbc:h2:mem:creator-channel-live-e2e;MODE=MySQL;NON_KEYWORDS=VALUE;DB_CLOSE_ON_EXIT=FALSE"
|
||||||
|
]
|
||||||
|
)
|
||||||
|
@AutoConfigureMockMvc
|
||||||
|
@ContextConfiguration(initializers = [EmbeddedRedisInitializer::class])
|
||||||
|
class CreatorChannelLiveEndToEndTest @Autowired constructor(
|
||||||
|
private val mockMvc: MockMvc,
|
||||||
|
private val entityManager: EntityManager,
|
||||||
|
private val transactionTemplate: TransactionTemplate
|
||||||
|
) {
|
||||||
|
@Test
|
||||||
|
@DisplayName("라이브 탭 API는 controller-service-repository를 거쳐 대표 응답을 반환한다")
|
||||||
|
fun shouldReturnLiveTabThroughControllerServiceAndRepository() {
|
||||||
|
val fixture = createFixture()
|
||||||
|
|
||||||
|
mockMvc.perform(
|
||||||
|
get("/api/v2/creator-channels/${fixture.creatorId}/live")
|
||||||
|
.param("sort", "LATEST")
|
||||||
|
.param("page", "0")
|
||||||
|
.param("size", "20")
|
||||||
|
.with(user(MemberAdapter(fixture.viewer)))
|
||||||
|
)
|
||||||
|
.andExpect(status().isOk)
|
||||||
|
.andExpect(jsonPath("$.success").value(true))
|
||||||
|
.andExpect(jsonPath("$.data.liveReplayContentCount").value(21))
|
||||||
|
.andExpect(jsonPath("$.data.currentLive.liveId").value(fixture.currentLiveId))
|
||||||
|
.andExpect(jsonPath("$.data.currentLive.title").value("e2e-live"))
|
||||||
|
.andExpect(jsonPath("$.data.currentLive.coverImageUrl").value("https://cdn.test/live-cover.png"))
|
||||||
|
.andExpect(jsonPath("$.data.liveReplayContents.length()").value(20))
|
||||||
|
.andExpect(jsonPath("$.data.liveReplayContents[0].audioContentId").value(fixture.keepContentId))
|
||||||
|
.andExpect(jsonPath("$.data.liveReplayContents[0].imageUrl").value("https://cdn.test/audio-1.png"))
|
||||||
|
.andExpect(jsonPath("$.data.liveReplayContents[0].isOwned").value(true))
|
||||||
|
.andExpect(jsonPath("$.data.liveReplayContents[0].isRented").value(false))
|
||||||
|
.andExpect(jsonPath("$.data.liveReplayContents[1].audioContentId").value(fixture.rentalContentId))
|
||||||
|
.andExpect(jsonPath("$.data.liveReplayContents[1].isOwned").value(false))
|
||||||
|
.andExpect(jsonPath("$.data.liveReplayContents[1].isRented").value(true))
|
||||||
|
.andExpect(jsonPath("$.data.liveReplayContents[2].audioContentId").value(fixture.unorderedContentId))
|
||||||
|
.andExpect(jsonPath("$.data.liveReplayContents[2].isOwned").value(false))
|
||||||
|
.andExpect(jsonPath("$.data.liveReplayContents[2].isRented").value(false))
|
||||||
|
.andExpect(jsonPath("$.data.sort").value("LATEST"))
|
||||||
|
.andExpect(jsonPath("$.data.page").value(0))
|
||||||
|
.andExpect(jsonPath("$.data.size").value(20))
|
||||||
|
.andExpect(jsonPath("$.data.hasNext").value(true))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createFixture(): Fixture {
|
||||||
|
return transactionTemplate.execute {
|
||||||
|
val now = LocalDateTime.now()
|
||||||
|
val viewer = saveMember("live-e2e-viewer", MemberRole.USER)
|
||||||
|
val creator = saveMember("live-e2e-creator", MemberRole.CREATOR)
|
||||||
|
val currentLive = saveLiveRoom(creator, now.minusHours(1))
|
||||||
|
val liveReplayTheme = saveTheme("다시듣기")
|
||||||
|
val contents = (1..21).map { index ->
|
||||||
|
saveAudioContent(
|
||||||
|
creator = creator,
|
||||||
|
releaseDate = now.minusMinutes(index.toLong()),
|
||||||
|
theme = liveReplayTheme,
|
||||||
|
coverImage = "audio-$index.png"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
saveOrder(viewer, creator, contents[0], OrderType.KEEP)
|
||||||
|
saveOrder(viewer, creator, contents[1], OrderType.RENTAL, endDate = now.plusDays(1))
|
||||||
|
entityManager.flush()
|
||||||
|
|
||||||
|
Fixture(
|
||||||
|
viewer = viewer,
|
||||||
|
creatorId = creator.id!!,
|
||||||
|
currentLiveId = currentLive.id!!,
|
||||||
|
keepContentId = contents[0].id!!,
|
||||||
|
rentalContentId = contents[1].id!!,
|
||||||
|
unorderedContentId = contents[2].id!!
|
||||||
|
)
|
||||||
|
}!!
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun saveMember(nickname: String, role: MemberRole): Member {
|
||||||
|
val member = Member(
|
||||||
|
email = "$nickname@test.com",
|
||||||
|
password = "password",
|
||||||
|
nickname = nickname,
|
||||||
|
role = role
|
||||||
|
)
|
||||||
|
entityManager.persist(member)
|
||||||
|
return member
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun saveLiveRoom(creator: Member, beginDateTime: LocalDateTime): LiveRoom {
|
||||||
|
val liveRoom = LiveRoom(
|
||||||
|
title = "e2e-live",
|
||||||
|
notice = "notice",
|
||||||
|
beginDateTime = beginDateTime,
|
||||||
|
numberOfPeople = 0,
|
||||||
|
coverImage = "live-cover.png",
|
||||||
|
isAdult = false,
|
||||||
|
price = 50,
|
||||||
|
isAvailableJoinCreator = true,
|
||||||
|
genderRestriction = GenderRestriction.ALL
|
||||||
|
)
|
||||||
|
liveRoom.member = creator
|
||||||
|
liveRoom.channelName = "e2e-live-channel"
|
||||||
|
liveRoom.isActive = true
|
||||||
|
entityManager.persist(liveRoom)
|
||||||
|
return liveRoom
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun saveAudioContent(
|
||||||
|
creator: Member,
|
||||||
|
releaseDate: LocalDateTime,
|
||||||
|
theme: AudioContentTheme,
|
||||||
|
coverImage: String
|
||||||
|
): AudioContent {
|
||||||
|
val content = AudioContent(
|
||||||
|
title = "audio-$coverImage",
|
||||||
|
detail = "detail",
|
||||||
|
languageCode = "ko",
|
||||||
|
releaseDate = releaseDate,
|
||||||
|
isAdult = false,
|
||||||
|
price = 100,
|
||||||
|
isPointAvailable = true
|
||||||
|
)
|
||||||
|
content.member = creator
|
||||||
|
content.theme = theme
|
||||||
|
content.isActive = true
|
||||||
|
content.coverImage = coverImage
|
||||||
|
content.duration = "00:10:00"
|
||||||
|
entityManager.persist(content)
|
||||||
|
return content
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun saveTheme(name: String): AudioContentTheme {
|
||||||
|
val theme = AudioContentTheme(theme = name, image = "$name.png", isActive = true)
|
||||||
|
entityManager.persist(theme)
|
||||||
|
return theme
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun saveOrder(
|
||||||
|
member: Member,
|
||||||
|
creator: Member,
|
||||||
|
content: AudioContent,
|
||||||
|
type: OrderType,
|
||||||
|
endDate: LocalDateTime? = null
|
||||||
|
): Order {
|
||||||
|
val order = Order(type = type, isActive = true)
|
||||||
|
order.member = member
|
||||||
|
order.creator = creator
|
||||||
|
order.audioContent = content
|
||||||
|
entityManager.persist(order)
|
||||||
|
endDate?.let { order.endDate = it }
|
||||||
|
return order
|
||||||
|
}
|
||||||
|
|
||||||
|
private data class Fixture(
|
||||||
|
val viewer: Member,
|
||||||
|
val creatorId: Long,
|
||||||
|
val currentLiveId: Long,
|
||||||
|
val keepContentId: Long,
|
||||||
|
val rentalContentId: Long,
|
||||||
|
val unorderedContentId: Long
|
||||||
|
)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user