test #426
@@ -0,0 +1,142 @@
|
|||||||
|
package kr.co.vividnext.sodalive.v2.api.home.following.dto
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty
|
||||||
|
import kr.co.vividnext.sodalive.v2.chat.dto.ChatRoomListItemResponse
|
||||||
|
import kr.co.vividnext.sodalive.v2.common.domain.CreatorActivityType
|
||||||
|
import kr.co.vividnext.sodalive.v2.home.following.domain.FollowingNewsType
|
||||||
|
import kr.co.vividnext.sodalive.v2.home.following.domain.HomeFollowing
|
||||||
|
import kr.co.vividnext.sodalive.v2.home.following.domain.HomeFollowingCreator
|
||||||
|
import kr.co.vividnext.sodalive.v2.home.following.domain.HomeFollowingLive
|
||||||
|
import kr.co.vividnext.sodalive.v2.home.following.domain.HomeFollowingNews
|
||||||
|
import kr.co.vividnext.sodalive.v2.home.following.domain.HomeFollowingSchedule
|
||||||
|
|
||||||
|
data class HomeFollowingTabResponse(
|
||||||
|
@JsonProperty("isLoginRequired")
|
||||||
|
val isLoginRequired: Boolean,
|
||||||
|
val followingCreators: List<FollowingCreatorResponse>,
|
||||||
|
val onAirLives: List<FollowingLiveResponse>,
|
||||||
|
val recentChats: List<ChatRoomListItemResponse>,
|
||||||
|
val monthlySchedules: List<FollowingScheduleResponse>,
|
||||||
|
val recentNews: List<FollowingNewsResponse>
|
||||||
|
) {
|
||||||
|
companion object {
|
||||||
|
fun loginRequired(): HomeFollowingTabResponse {
|
||||||
|
return HomeFollowingTabResponse(
|
||||||
|
isLoginRequired = true,
|
||||||
|
followingCreators = emptyList(),
|
||||||
|
onAirLives = emptyList(),
|
||||||
|
recentChats = emptyList(),
|
||||||
|
monthlySchedules = emptyList(),
|
||||||
|
recentNews = emptyList()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun from(home: HomeFollowing): HomeFollowingTabResponse {
|
||||||
|
return HomeFollowingTabResponse(
|
||||||
|
isLoginRequired = false,
|
||||||
|
followingCreators = home.followingCreators.map(FollowingCreatorResponse::from),
|
||||||
|
onAirLives = home.onAirLives.map(FollowingLiveResponse::from),
|
||||||
|
recentChats = home.recentChats,
|
||||||
|
monthlySchedules = home.monthlySchedules.map(FollowingScheduleResponse::from),
|
||||||
|
recentNews = home.recentNews.map(FollowingNewsResponse::from)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class FollowingCreatorResponse(
|
||||||
|
val creatorId: Long,
|
||||||
|
val creatorNickname: String,
|
||||||
|
val creatorProfileImageUrl: String
|
||||||
|
) {
|
||||||
|
companion object {
|
||||||
|
fun from(creator: HomeFollowingCreator): FollowingCreatorResponse {
|
||||||
|
return FollowingCreatorResponse(
|
||||||
|
creatorId = creator.creatorId,
|
||||||
|
creatorNickname = creator.creatorNickname,
|
||||||
|
creatorProfileImageUrl = creator.creatorProfileImageUrl
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class FollowingLiveResponse(
|
||||||
|
val liveId: Long,
|
||||||
|
val creatorProfileImageUrl: String,
|
||||||
|
val creatorNickname: String,
|
||||||
|
val title: String,
|
||||||
|
val startedAtUtc: String
|
||||||
|
) {
|
||||||
|
companion object {
|
||||||
|
fun from(live: HomeFollowingLive): FollowingLiveResponse {
|
||||||
|
return FollowingLiveResponse(
|
||||||
|
liveId = live.liveId,
|
||||||
|
creatorProfileImageUrl = live.creatorProfileImageUrl,
|
||||||
|
creatorNickname = live.creatorNickname,
|
||||||
|
title = live.title,
|
||||||
|
startedAtUtc = live.startedAtUtc
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class FollowingScheduleResponse(
|
||||||
|
val scheduleId: String,
|
||||||
|
val creatorId: Long,
|
||||||
|
val creatorProfileImageUrl: String,
|
||||||
|
val creatorNickname: String,
|
||||||
|
val title: String,
|
||||||
|
val type: CreatorActivityType,
|
||||||
|
val targetId: Long,
|
||||||
|
val scheduledAtUtc: String,
|
||||||
|
@JsonProperty("isOnAir")
|
||||||
|
val isOnAir: Boolean
|
||||||
|
) {
|
||||||
|
companion object {
|
||||||
|
fun from(schedule: HomeFollowingSchedule): FollowingScheduleResponse {
|
||||||
|
return FollowingScheduleResponse(
|
||||||
|
scheduleId = schedule.scheduleId,
|
||||||
|
creatorId = schedule.creatorId,
|
||||||
|
creatorProfileImageUrl = schedule.creatorProfileImageUrl,
|
||||||
|
creatorNickname = schedule.creatorNickname,
|
||||||
|
title = schedule.title,
|
||||||
|
type = schedule.type,
|
||||||
|
targetId = schedule.targetId,
|
||||||
|
scheduledAtUtc = schedule.scheduledAtUtc,
|
||||||
|
isOnAir = schedule.isOnAir
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class FollowingNewsResponse(
|
||||||
|
val newsId: String,
|
||||||
|
val type: FollowingNewsType,
|
||||||
|
val creatorProfileImageUrl: String,
|
||||||
|
val creatorNickname: String,
|
||||||
|
val title: String,
|
||||||
|
val body: String,
|
||||||
|
val thumbnailImageUrl: String?,
|
||||||
|
val targetId: Long,
|
||||||
|
val occurredAtUtc: String,
|
||||||
|
val visibleFromAtUtc: String,
|
||||||
|
val rank: Int?
|
||||||
|
) {
|
||||||
|
companion object {
|
||||||
|
fun from(news: HomeFollowingNews): FollowingNewsResponse {
|
||||||
|
return FollowingNewsResponse(
|
||||||
|
newsId = news.newsId,
|
||||||
|
type = news.type,
|
||||||
|
creatorProfileImageUrl = news.creatorProfileImageUrl,
|
||||||
|
creatorNickname = news.creatorNickname,
|
||||||
|
title = news.title,
|
||||||
|
body = news.body,
|
||||||
|
thumbnailImageUrl = news.thumbnailImageUrl,
|
||||||
|
targetId = news.targetId,
|
||||||
|
occurredAtUtc = news.occurredAtUtc,
|
||||||
|
visibleFromAtUtc = news.visibleFromAtUtc,
|
||||||
|
rank = news.rank
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package kr.co.vividnext.sodalive.v2.home.following.domain
|
||||||
|
|
||||||
|
enum class FollowingNewsType {
|
||||||
|
CREATOR_RANKING,
|
||||||
|
CONTENT_RANKING,
|
||||||
|
COMMUNITY_POST,
|
||||||
|
AUDIO_CONTENT,
|
||||||
|
PHOTO_CONTENT
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
package kr.co.vividnext.sodalive.v2.home.following.domain
|
||||||
|
|
||||||
|
import kr.co.vividnext.sodalive.v2.chat.dto.ChatRoomListItemResponse
|
||||||
|
import kr.co.vividnext.sodalive.v2.common.domain.CreatorActivityType
|
||||||
|
|
||||||
|
data class HomeFollowing(
|
||||||
|
val followingCreators: List<HomeFollowingCreator>,
|
||||||
|
val onAirLives: List<HomeFollowingLive>,
|
||||||
|
val recentChats: List<ChatRoomListItemResponse>,
|
||||||
|
val monthlySchedules: List<HomeFollowingSchedule>,
|
||||||
|
val recentNews: List<HomeFollowingNews>
|
||||||
|
)
|
||||||
|
|
||||||
|
data class HomeFollowingCreator(
|
||||||
|
val creatorId: Long,
|
||||||
|
val creatorNickname: String,
|
||||||
|
val creatorProfileImageUrl: String
|
||||||
|
)
|
||||||
|
|
||||||
|
data class HomeFollowingLive(
|
||||||
|
val liveId: Long,
|
||||||
|
val creatorProfileImageUrl: String,
|
||||||
|
val creatorNickname: String,
|
||||||
|
val title: String,
|
||||||
|
val startedAtUtc: String
|
||||||
|
)
|
||||||
|
|
||||||
|
data class HomeFollowingSchedule(
|
||||||
|
val scheduleId: String,
|
||||||
|
val creatorId: Long,
|
||||||
|
val creatorProfileImageUrl: String,
|
||||||
|
val creatorNickname: String,
|
||||||
|
val title: String,
|
||||||
|
val type: CreatorActivityType,
|
||||||
|
val targetId: Long,
|
||||||
|
val scheduledAtUtc: String,
|
||||||
|
val isOnAir: Boolean
|
||||||
|
)
|
||||||
|
|
||||||
|
data class HomeFollowingNews(
|
||||||
|
val newsId: String,
|
||||||
|
val type: FollowingNewsType,
|
||||||
|
val creatorProfileImageUrl: String,
|
||||||
|
val creatorNickname: String,
|
||||||
|
val title: String,
|
||||||
|
val body: String,
|
||||||
|
val thumbnailImageUrl: String?,
|
||||||
|
val targetId: Long,
|
||||||
|
val occurredAtUtc: String,
|
||||||
|
val visibleFromAtUtc: String,
|
||||||
|
val rank: Int?
|
||||||
|
)
|
||||||
@@ -0,0 +1,114 @@
|
|||||||
|
package kr.co.vividnext.sodalive.v2.api.home.following.dto
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
||||||
|
import kr.co.vividnext.sodalive.v2.chat.dto.ChatRoomListItemResponse
|
||||||
|
import kr.co.vividnext.sodalive.v2.common.domain.CreatorActivityType
|
||||||
|
import kr.co.vividnext.sodalive.v2.home.following.domain.FollowingNewsType
|
||||||
|
import kr.co.vividnext.sodalive.v2.home.following.domain.HomeFollowing
|
||||||
|
import kr.co.vividnext.sodalive.v2.home.following.domain.HomeFollowingCreator
|
||||||
|
import kr.co.vividnext.sodalive.v2.home.following.domain.HomeFollowingLive
|
||||||
|
import kr.co.vividnext.sodalive.v2.home.following.domain.HomeFollowingNews
|
||||||
|
import kr.co.vividnext.sodalive.v2.home.following.domain.HomeFollowingSchedule
|
||||||
|
import org.junit.jupiter.api.Assertions.assertEquals
|
||||||
|
import org.junit.jupiter.api.Assertions.assertFalse
|
||||||
|
import org.junit.jupiter.api.Assertions.assertTrue
|
||||||
|
import org.junit.jupiter.api.DisplayName
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
|
||||||
|
class HomeFollowingTabResponseTest {
|
||||||
|
private val objectMapper = jacksonObjectMapper()
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("비로그인 응답은 로그인이 필요하며 모든 섹션을 빈 배열로 반환한다")
|
||||||
|
fun shouldReturnLoginRequiredResponseWithEmptySections() {
|
||||||
|
val response = HomeFollowingTabResponse.loginRequired()
|
||||||
|
val json = objectMapper.readTree(objectMapper.writeValueAsString(response))
|
||||||
|
|
||||||
|
assertTrue(response.isLoginRequired)
|
||||||
|
assertTrue(response.followingCreators.isEmpty())
|
||||||
|
assertTrue(response.onAirLives.isEmpty())
|
||||||
|
assertTrue(response.recentChats.isEmpty())
|
||||||
|
assertTrue(response.monthlySchedules.isEmpty())
|
||||||
|
assertTrue(response.recentNews.isEmpty())
|
||||||
|
assertEquals(true, json["isLoginRequired"].asBoolean())
|
||||||
|
assertTrue(json["followingCreators"].isArray)
|
||||||
|
assertTrue(json["onAirLives"].isArray)
|
||||||
|
assertTrue(json["recentChats"].isArray)
|
||||||
|
assertTrue(json["monthlySchedules"].isArray)
|
||||||
|
assertTrue(json["recentNews"].isArray)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("팔로잉 탭 도메인은 creatorId 없는 최근 소식과 nullable rank 응답으로 변환한다")
|
||||||
|
fun shouldMapDomainToResponseWithoutCreatorIdInRecentNews() {
|
||||||
|
val response = HomeFollowingTabResponse.from(createHomeFollowing())
|
||||||
|
val json = objectMapper.readTree(objectMapper.writeValueAsString(response))
|
||||||
|
|
||||||
|
assertFalse(response.isLoginRequired)
|
||||||
|
assertEquals(1L, response.followingCreators.first().creatorId)
|
||||||
|
assertEquals(10L, response.onAirLives.first().liveId)
|
||||||
|
assertEquals(100L, response.recentChats.first().roomId)
|
||||||
|
assertEquals("LIVE:20", response.monthlySchedules.first().scheduleId)
|
||||||
|
assertEquals(3, response.recentNews.first().rank)
|
||||||
|
assertEquals(false, json["isLoginRequired"].asBoolean())
|
||||||
|
assertFalse(json["recentNews"][0].has("creatorId"))
|
||||||
|
assertFalse(json["recentNews"][0].has("ranking"))
|
||||||
|
assertFalse(json["recentNews"][0].has("rankChange"))
|
||||||
|
assertFalse(json["recentNews"][0].has("isNew"))
|
||||||
|
assertEquals(3, json["recentNews"][0]["rank"].asInt())
|
||||||
|
assertEquals(true, json["monthlySchedules"][0]["isOnAir"].asBoolean())
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createHomeFollowing(): HomeFollowing {
|
||||||
|
return HomeFollowing(
|
||||||
|
followingCreators = listOf(HomeFollowingCreator(1L, "creator", "https://cdn/profile.jpg")),
|
||||||
|
onAirLives = listOf(
|
||||||
|
HomeFollowingLive(
|
||||||
|
liveId = 10L,
|
||||||
|
creatorProfileImageUrl = "https://cdn/live-profile.jpg",
|
||||||
|
creatorNickname = "live-creator",
|
||||||
|
title = "live title",
|
||||||
|
startedAtUtc = "2026-06-25T00:00:00Z"
|
||||||
|
)
|
||||||
|
),
|
||||||
|
recentChats = listOf(
|
||||||
|
ChatRoomListItemResponse(
|
||||||
|
roomId = 100L,
|
||||||
|
chatType = "DM",
|
||||||
|
targetName = "creator",
|
||||||
|
targetImageUrl = "https://cdn/chat.jpg",
|
||||||
|
lastMessage = "hello",
|
||||||
|
lastMessageAt = "2026-06-25T00:01:00Z"
|
||||||
|
)
|
||||||
|
),
|
||||||
|
monthlySchedules = listOf(
|
||||||
|
HomeFollowingSchedule(
|
||||||
|
scheduleId = "LIVE:20",
|
||||||
|
creatorId = 1L,
|
||||||
|
creatorProfileImageUrl = "https://cdn/schedule.jpg",
|
||||||
|
creatorNickname = "schedule-creator",
|
||||||
|
title = "schedule title",
|
||||||
|
type = CreatorActivityType.LIVE,
|
||||||
|
targetId = 20L,
|
||||||
|
scheduledAtUtc = "2026-06-26T00:00:00Z",
|
||||||
|
isOnAir = true
|
||||||
|
)
|
||||||
|
),
|
||||||
|
recentNews = listOf(
|
||||||
|
HomeFollowingNews(
|
||||||
|
newsId = "30",
|
||||||
|
type = FollowingNewsType.CREATOR_RANKING,
|
||||||
|
creatorProfileImageUrl = "https://cdn/news-profile.jpg",
|
||||||
|
creatorNickname = "news-creator",
|
||||||
|
title = "ranking",
|
||||||
|
body = "ranked",
|
||||||
|
thumbnailImageUrl = null,
|
||||||
|
targetId = 1L,
|
||||||
|
occurredAtUtc = "2026-06-25T00:00:00Z",
|
||||||
|
visibleFromAtUtc = "2026-06-25T09:00:00Z",
|
||||||
|
rank = 3
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user