feat(home-following): 팔로잉 탭 조회 service를 추가한다
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
package kr.co.vividnext.sodalive.v2.home.following.application
|
||||
|
||||
import kr.co.vividnext.sodalive.member.Member
|
||||
import kr.co.vividnext.sodalive.member.contentpreference.MemberContentPreferenceService
|
||||
import kr.co.vividnext.sodalive.v2.home.following.domain.HomeFollowing
|
||||
import kr.co.vividnext.sodalive.v2.home.following.port.out.HomeFollowingQueryPort
|
||||
import org.springframework.stereotype.Service
|
||||
import org.springframework.transaction.annotation.Transactional
|
||||
import java.time.Clock
|
||||
import java.time.LocalDateTime
|
||||
|
||||
@Service
|
||||
@Transactional(readOnly = true)
|
||||
class HomeFollowingQueryService(
|
||||
private val queryPort: HomeFollowingQueryPort,
|
||||
private val memberContentPreferenceService: MemberContentPreferenceService,
|
||||
private val nowProvider: () -> LocalDateTime = { LocalDateTime.now(Clock.systemUTC()) }
|
||||
) {
|
||||
fun findHomeFollowing(member: Member): HomeFollowing {
|
||||
val memberId = requireNotNull(member.id)
|
||||
val canViewAdultContent = memberContentPreferenceService.canViewAdultContent(member)
|
||||
val now = nowProvider()
|
||||
|
||||
return HomeFollowing(
|
||||
followingCreators = queryPort.findFollowingCreators(memberId, FOLLOWING_CREATORS_LIMIT),
|
||||
onAirLives = queryPort.findOnAirLives(memberId, canViewAdultContent, ON_AIR_LIVES_LIMIT),
|
||||
recentChats = emptyList(),
|
||||
monthlySchedules = queryPort.findMonthlySchedules(memberId, canViewAdultContent, now, MONTHLY_SCHEDULES_LIMIT),
|
||||
recentNews = queryPort.findRecentNews(memberId, canViewAdultContent, now, RECENT_NEWS_LIMIT)
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val FOLLOWING_CREATORS_LIMIT = 20
|
||||
private const val ON_AIR_LIVES_LIMIT = 10
|
||||
private const val MONTHLY_SCHEDULES_LIMIT = 3
|
||||
private const val RECENT_NEWS_LIMIT = 30
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
package kr.co.vividnext.sodalive.v2.home.following.application
|
||||
|
||||
import kr.co.vividnext.sodalive.member.Member
|
||||
import kr.co.vividnext.sodalive.member.contentpreference.MemberContentPreferenceService
|
||||
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 kr.co.vividnext.sodalive.v2.home.following.port.out.HomeFollowingQueryPort
|
||||
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
|
||||
import org.mockito.Mockito
|
||||
import java.time.LocalDateTime
|
||||
import java.time.ZoneOffset
|
||||
import java.util.TimeZone
|
||||
|
||||
class HomeFollowingQueryServiceTest {
|
||||
private val queryPort = RecordingHomeFollowingQueryPort()
|
||||
private val memberContentPreferenceService = Mockito.mock(MemberContentPreferenceService::class.java)
|
||||
private val now = LocalDateTime.of(2026, 6, 25, 12, 0)
|
||||
private val service = HomeFollowingQueryService(
|
||||
queryPort,
|
||||
memberContentPreferenceService
|
||||
) { now }
|
||||
|
||||
@Test
|
||||
@DisplayName("팔로잉 탭 조회는 각 섹션의 기본 limit와 고정 now를 port에 전달한다")
|
||||
fun shouldCallQueryPortWithDefaultLimitsAndNow() {
|
||||
val member = member(10L)
|
||||
Mockito.`when`(memberContentPreferenceService.canViewAdultContent(member)).thenReturn(true)
|
||||
|
||||
val home = service.findHomeFollowing(member)
|
||||
|
||||
assertEquals(listOf(HomeFollowingCreator(1L, "creator", "profile")), home.followingCreators)
|
||||
assertEquals(emptyList<Any>(), home.recentChats)
|
||||
assertEquals(20, queryPort.followingCreatorsLimit)
|
||||
assertEquals(10, queryPort.onAirLivesLimit)
|
||||
assertEquals(3, queryPort.monthlySchedulesLimit)
|
||||
assertEquals(30, queryPort.recentNewsLimit)
|
||||
assertEquals(now, queryPort.monthlySchedulesNow)
|
||||
assertEquals(now, queryPort.recentNewsNow)
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("성인 콘텐츠 노출 가능 여부는 On Air, 스케줄, 최근 소식 조회에 전달된다")
|
||||
fun shouldPassAdultContentVisibilityToQueryPort() {
|
||||
val member = member(11L)
|
||||
Mockito.`when`(memberContentPreferenceService.canViewAdultContent(member)).thenReturn(false)
|
||||
|
||||
service.findHomeFollowing(member)
|
||||
|
||||
assertEquals(false, queryPort.onAirCanViewAdultContent)
|
||||
assertEquals(false, queryPort.monthlySchedulesCanViewAdultContent)
|
||||
assertEquals(false, queryPort.recentNewsCanViewAdultContent)
|
||||
assertEquals(11L, queryPort.memberId)
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("기본 now는 JVM 기본 timezone과 무관하게 UTC 기준으로 port에 전달된다")
|
||||
fun shouldUseUtcNowRegardlessOfJvmDefaultTimezone() {
|
||||
val originalTimeZone = TimeZone.getDefault()
|
||||
try {
|
||||
TimeZone.setDefault(TimeZone.getTimeZone("Asia/Seoul"))
|
||||
val defaultQueryPort = RecordingHomeFollowingQueryPort()
|
||||
val defaultService = HomeFollowingQueryService(defaultQueryPort, memberContentPreferenceService)
|
||||
val member = member(12L)
|
||||
Mockito.`when`(memberContentPreferenceService.canViewAdultContent(member)).thenReturn(true)
|
||||
val beforeUtc = LocalDateTime.now(ZoneOffset.UTC).minusSeconds(1)
|
||||
|
||||
defaultService.findHomeFollowing(member)
|
||||
|
||||
val afterUtc = LocalDateTime.now(ZoneOffset.UTC).plusSeconds(1)
|
||||
val capturedNow = defaultQueryPort.recentNewsNow!!
|
||||
assertFalse(capturedNow.isBefore(beforeUtc))
|
||||
assertFalse(capturedNow.isAfter(afterUtc))
|
||||
assertTrue(LocalDateTime.now().isAfter(capturedNow.plusHours(8)))
|
||||
} finally {
|
||||
TimeZone.setDefault(originalTimeZone)
|
||||
}
|
||||
}
|
||||
|
||||
private fun member(id: Long): Member {
|
||||
return Member(email = "member-$id@test.com", password = "password", nickname = "member-$id").apply { this.id = id }
|
||||
}
|
||||
|
||||
private class RecordingHomeFollowingQueryPort : HomeFollowingQueryPort {
|
||||
var memberId: Long? = null
|
||||
var followingCreatorsLimit: Int? = null
|
||||
var onAirLivesLimit: Int? = null
|
||||
var onAirCanViewAdultContent: Boolean? = null
|
||||
var monthlySchedulesLimit: Int? = null
|
||||
var monthlySchedulesNow: LocalDateTime? = null
|
||||
var monthlySchedulesCanViewAdultContent: Boolean? = null
|
||||
var recentNewsLimit: Int? = null
|
||||
var recentNewsNow: LocalDateTime? = null
|
||||
var recentNewsCanViewAdultContent: Boolean? = null
|
||||
|
||||
override fun findFollowingCreators(memberId: Long, limit: Int): List<HomeFollowingCreator> {
|
||||
this.memberId = memberId
|
||||
followingCreatorsLimit = limit
|
||||
return listOf(HomeFollowingCreator(1L, "creator", "profile"))
|
||||
}
|
||||
|
||||
override fun findOnAirLives(memberId: Long, canViewAdultContent: Boolean, limit: Int): List<HomeFollowingLive> {
|
||||
onAirCanViewAdultContent = canViewAdultContent
|
||||
onAirLivesLimit = limit
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
override fun findMonthlySchedules(
|
||||
memberId: Long,
|
||||
canViewAdultContent: Boolean,
|
||||
now: LocalDateTime,
|
||||
limit: Int
|
||||
): List<HomeFollowingSchedule> {
|
||||
monthlySchedulesCanViewAdultContent = canViewAdultContent
|
||||
monthlySchedulesNow = now
|
||||
monthlySchedulesLimit = limit
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
override fun findRecentNews(
|
||||
memberId: Long,
|
||||
canViewAdultContent: Boolean,
|
||||
nowUtc: LocalDateTime,
|
||||
limit: Int
|
||||
): List<HomeFollowingNews> {
|
||||
recentNewsCanViewAdultContent = canViewAdultContent
|
||||
recentNewsNow = nowUtc
|
||||
recentNewsLimit = limit
|
||||
return emptyList()
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user